parent
67678cd49e
commit
bbe180ecd1
4 changed files with 133 additions and 33 deletions
|
@ -31,12 +31,14 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.library_controller.view.*
|
||||
import rx.Subscription
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -118,6 +120,10 @@ class LibraryController(
|
|||
*/
|
||||
private var drawerListener: DrawerLayout.DrawerListener? = null
|
||||
|
||||
private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create()
|
||||
|
||||
private var tabsVisibilitySubscription: Subscription? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
@ -173,6 +179,8 @@ class LibraryController(
|
|||
super.onDestroyView(view)
|
||||
adapter = null
|
||||
actionMode = null
|
||||
tabsVisibilitySubscription?.unsubscribe()
|
||||
tabsVisibilitySubscription = null
|
||||
}
|
||||
|
||||
override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup {
|
||||
|
@ -204,6 +212,27 @@ class LibraryController(
|
|||
navView = null
|
||||
}
|
||||
|
||||
override fun configureTabs(tabs: TabLayout) {
|
||||
with(tabs) {
|
||||
tabGravity = TabLayout.GRAVITY_CENTER
|
||||
tabMode = TabLayout.MODE_SCROLLABLE
|
||||
}
|
||||
tabsVisibilitySubscription?.unsubscribe()
|
||||
tabsVisibilitySubscription = tabsVisibilityRelay.subscribe { visible ->
|
||||
val tabAnimator = (activity as? MainActivity)?.tabAnimator
|
||||
if (visible) {
|
||||
tabAnimator?.expand()
|
||||
} else {
|
||||
tabAnimator?.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanupTabs(tabs: TabLayout) {
|
||||
tabsVisibilitySubscription?.unsubscribe()
|
||||
tabsVisibilitySubscription = null
|
||||
}
|
||||
|
||||
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>) {
|
||||
val view = view ?: return
|
||||
val adapter = adapter ?: return
|
||||
|
@ -227,7 +256,7 @@ class LibraryController(
|
|||
// Restore active category.
|
||||
view.view_pager.setCurrentItem(activeCat, false)
|
||||
|
||||
tabs?.visibility = if (categories.size <= 1) View.GONE else View.VISIBLE
|
||||
tabsVisibilityRelay.call(categories.size > 1)
|
||||
|
||||
// Delay the scroll position to allow the view to be properly measured.
|
||||
view.post {
|
||||
|
@ -282,13 +311,6 @@ class LibraryController(
|
|||
adapter.recycle = true
|
||||
}
|
||||
|
||||
override fun configureTabs(tabs: TabLayout) {
|
||||
with(tabs) {
|
||||
tabGravity = TabLayout.GRAVITY_CENTER
|
||||
tabMode = TabLayout.MODE_SCROLLABLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the action mode if it's not created already.
|
||||
*/
|
||||
|
|
|
@ -49,7 +49,7 @@ class MainActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private val tabAnimator by lazy { TabsAnimator(tabs) }
|
||||
lateinit var tabAnimator: TabsAnimator
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setAppTheme()
|
||||
|
@ -69,6 +69,8 @@ class MainActivity : BaseActivity() {
|
|||
drawerArrow?.color = Color.WHITE
|
||||
toolbar.navigationIcon = drawerArrow
|
||||
|
||||
tabAnimator = TabsAnimator(tabs)
|
||||
|
||||
// Set behavior of Navigation drawer
|
||||
nav_view.setNavigationItemSelectedListener { item ->
|
||||
val id = item.itemId
|
||||
|
@ -190,8 +192,8 @@ class MainActivity : BaseActivity() {
|
|||
from.cleanupTabs(tabs)
|
||||
}
|
||||
if (to is TabbedController) {
|
||||
to.configureTabs(tabs)
|
||||
tabAnimator.expand()
|
||||
to.configureTabs(tabs)
|
||||
} else {
|
||||
tabAnimator.collapse()
|
||||
tabs.setupWithViewPager(null)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package eu.kanade.tachiyomi.ui.main
|
||||
|
||||
import android.support.design.widget.TabLayout
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.Transformation
|
||||
|
@ -9,15 +11,32 @@ import eu.kanade.tachiyomi.util.visible
|
|||
|
||||
class TabsAnimator(val tabs: TabLayout) {
|
||||
|
||||
private var height = 0
|
||||
/**
|
||||
* The default height of the tab layout. It's unknown until the view is layout.
|
||||
*/
|
||||
private var tabsHeight = 0
|
||||
|
||||
/**
|
||||
* Whether the last state of the tab layout is [View.VISIBLE] or [View.GONE].
|
||||
*/
|
||||
private var isLastStateShown = true
|
||||
|
||||
/**
|
||||
* Interpolator used to animate the tab layout.
|
||||
*/
|
||||
private val interpolator = DecelerateInterpolator()
|
||||
|
||||
/**
|
||||
* Duration of the animation.
|
||||
*/
|
||||
private val duration = 300L
|
||||
|
||||
/**
|
||||
* Animation used to expand the tab layout.
|
||||
*/
|
||||
private val expandAnimation = object : Animation() {
|
||||
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||
tabs.layoutParams.height = (height * interpolatedTime).toInt()
|
||||
setHeight((tabsHeight * interpolatedTime).toInt())
|
||||
tabs.requestLayout()
|
||||
}
|
||||
|
||||
|
@ -26,12 +45,24 @@ class TabsAnimator(val tabs: TabLayout) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animation used to collapse the tab layout.
|
||||
*/
|
||||
private val collapseAnimation = object : Animation() {
|
||||
|
||||
/**
|
||||
* Property holding the height of the tabs at the moment the animation is started. Useful
|
||||
* to provide a seamless animation.
|
||||
*/
|
||||
private var startHeight = 0
|
||||
|
||||
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||
if (interpolatedTime == 1f) {
|
||||
if (interpolatedTime == 0f) {
|
||||
startHeight = tabs.height
|
||||
} else if (interpolatedTime == 1f) {
|
||||
tabs.gone()
|
||||
} else {
|
||||
tabs.layoutParams.height = (height * (1 - interpolatedTime)).toInt()
|
||||
setHeight((startHeight * (1 - interpolatedTime)).toInt())
|
||||
tabs.requestLayout()
|
||||
}
|
||||
}
|
||||
|
@ -46,29 +77,75 @@ class TabsAnimator(val tabs: TabLayout) {
|
|||
collapseAnimation.interpolator = interpolator
|
||||
expandAnimation.duration = duration
|
||||
expandAnimation.interpolator = interpolator
|
||||
}
|
||||
|
||||
fun expand() {
|
||||
isLastStateShown = tabs.visibility == View.VISIBLE
|
||||
tabs.viewTreeObserver.addOnGlobalLayoutListener(
|
||||
object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
if (tabs.height > 0) {
|
||||
tabs.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
|
||||
// Save the tabs default height.
|
||||
tabsHeight = tabs.height
|
||||
|
||||
// Now that we know the height, set the initial height and visibility.
|
||||
if (isLastStateShown) {
|
||||
setHeight(tabsHeight)
|
||||
tabs.visible()
|
||||
if (measure() && tabs.measuredHeight != height) {
|
||||
tabs.startAnimation(expandAnimation)
|
||||
}
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
if (measure() && tabs.measuredHeight != 0) {
|
||||
tabs.startAnimation(collapseAnimation)
|
||||
} else {
|
||||
setHeight(0)
|
||||
tabs.gone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the view is measured, otherwise query dimensions and check again.
|
||||
* Sets the height of the tab layout.
|
||||
*
|
||||
* @param newHeight The new height of the tab layout.
|
||||
*/
|
||||
private fun measure(): Boolean {
|
||||
if (height > 0) return true
|
||||
height = tabs.measuredHeight
|
||||
return height > 0
|
||||
private fun setHeight(newHeight: Int) {
|
||||
tabs.layoutParams.height = newHeight
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the tab layout with an animation.
|
||||
*/
|
||||
fun expand() {
|
||||
cancelCurrentAnimations()
|
||||
tabs.visible()
|
||||
if (isMeasured && (!isLastStateShown || tabs.height != tabsHeight)) {
|
||||
tabs.startAnimation(expandAnimation)
|
||||
}
|
||||
isLastStateShown = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse the tab layout with an animation.
|
||||
*/
|
||||
fun collapse() {
|
||||
cancelCurrentAnimations()
|
||||
if (isMeasured && (isLastStateShown || tabs.height != 0)) {
|
||||
tabs.startAnimation(collapseAnimation)
|
||||
}
|
||||
isLastStateShown = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all the currently running animations.
|
||||
*/
|
||||
private fun cancelCurrentAnimations() {
|
||||
collapseAnimation.cancel()
|
||||
expandAnimation.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the tab layout has a known height.
|
||||
*/
|
||||
val isMeasured: Boolean
|
||||
get() = tabsHeight > 0
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@
|
|||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:theme="@style/Theme.ActionBar.Tab"
|
||||
app:tabIndicatorColor="@android:color/white"
|
||||
app:tabGravity="center"
|
||||
|
|
Reference in a new issue