Use an object animator for the tabs

This commit is contained in:
inorichi 2017-05-22 11:28:41 +02:00
parent 256a4197c9
commit dc5283ce9a
2 changed files with 25 additions and 76 deletions

View File

@ -120,7 +120,7 @@ class LibraryController(
*/ */
private var drawerListener: DrawerLayout.DrawerListener? = null private var drawerListener: DrawerLayout.DrawerListener? = null
private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create() private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create(false)
private var tabsVisibilitySubscription: Subscription? = null private var tabsVisibilitySubscription: Subscription? = null

View File

@ -1,13 +1,10 @@
package eu.kanade.tachiyomi.ui.main package eu.kanade.tachiyomi.ui.main
import android.animation.ObjectAnimator
import android.support.design.widget.TabLayout import android.support.design.widget.TabLayout
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.Transformation
import eu.kanade.tachiyomi.util.gone
import eu.kanade.tachiyomi.util.visible
class TabsAnimator(val tabs: TabLayout) { class TabsAnimator(val tabs: TabLayout) {
@ -17,67 +14,21 @@ class TabsAnimator(val tabs: TabLayout) {
private var tabsHeight = 0 private var tabsHeight = 0
/** /**
* Whether the last state of the tab layout is [View.VISIBLE] or [View.GONE]. * Whether the last state of the tab layout is shown or hidden.
*/ */
private var isLastStateShown = true private var isLastStateShown = true
/** /**
* Interpolator used to animate the tab layout. * Animation used to expand and collapse the tab layout.
*/ */
private val interpolator = DecelerateInterpolator() private val animation by lazy {
ObjectAnimator.ofInt(this, "height", tabsHeight).also {
/** it.duration = 300L
* Duration of the animation. it.interpolator = DecelerateInterpolator()
*/
private val duration = 300L
/**
* Animation used to expand the tab layout.
*/
private val expandAnimation = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
setHeight((tabsHeight * interpolatedTime).toInt())
tabs.requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}
/**
* 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 == 0f) {
startHeight = tabs.height
} else if (interpolatedTime == 1f) {
tabs.gone()
} else {
setHeight((startHeight * (1 - interpolatedTime)).toInt())
tabs.requestLayout()
}
}
override fun willChangeBounds(): Boolean {
return true
} }
} }
init { init {
collapseAnimation.duration = duration
collapseAnimation.interpolator = interpolator
expandAnimation.duration = duration
expandAnimation.interpolator = interpolator
isLastStateShown = tabs.visibility == View.VISIBLE isLastStateShown = tabs.visibility == View.VISIBLE
tabs.viewTreeObserver.addOnGlobalLayoutListener( tabs.viewTreeObserver.addOnGlobalLayoutListener(
object : ViewTreeObserver.OnGlobalLayoutListener { object : ViewTreeObserver.OnGlobalLayoutListener {
@ -91,10 +42,8 @@ class TabsAnimator(val tabs: TabLayout) {
// Now that we know the height, set the initial height and visibility. // Now that we know the height, set the initial height and visibility.
if (isLastStateShown) { if (isLastStateShown) {
setHeight(tabsHeight) setHeight(tabsHeight)
tabs.visible()
} else { } else {
setHeight(0) setHeight(0)
tabs.gone()
} }
} }
} }
@ -107,18 +56,26 @@ class TabsAnimator(val tabs: TabLayout) {
* *
* @param newHeight The new height of the tab layout. * @param newHeight The new height of the tab layout.
*/ */
private fun setHeight(newHeight: Int) { fun setHeight(newHeight: Int) {
tabs.layoutParams.height = newHeight tabs.layoutParams.height = newHeight
tabs.requestLayout()
}
/**
* Returns the height of the tab layout. This method is also called from the animator through
* reflection.
*/
fun getHeight(): Int {
return tabs.layoutParams.height
} }
/** /**
* Expands the tab layout with an animation. * Expands the tab layout with an animation.
*/ */
fun expand() { fun expand() {
cancelCurrentAnimations() if (isMeasured && (!isLastStateShown || getHeight() != tabsHeight)) {
tabs.visible() animation.setIntValues(tabsHeight)
if (isMeasured && (!isLastStateShown || tabs.height != tabsHeight)) { animation.start()
tabs.startAnimation(expandAnimation)
} }
isLastStateShown = true isLastStateShown = true
} }
@ -127,25 +84,17 @@ class TabsAnimator(val tabs: TabLayout) {
* Collapse the tab layout with an animation. * Collapse the tab layout with an animation.
*/ */
fun collapse() { fun collapse() {
cancelCurrentAnimations() if (isMeasured && (isLastStateShown || getHeight() != 0)) {
if (isMeasured && (isLastStateShown || tabs.height != 0)) { animation.setIntValues(0)
tabs.startAnimation(collapseAnimation) animation.start()
} }
isLastStateShown = false isLastStateShown = false
} }
/**
* Cancels all the currently running animations.
*/
private fun cancelCurrentAnimations() {
collapseAnimation.cancel()
expandAnimation.cancel()
}
/** /**
* Returns whether the tab layout has a known height. * Returns whether the tab layout has a known height.
*/ */
val isMeasured: Boolean private val isMeasured: Boolean
get() = tabsHeight > 0 get() = tabsHeight > 0
} }