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.SecondaryDrawerController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryController
|
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.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.util.inflate
|
import eu.kanade.tachiyomi.util.inflate
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener
|
import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.library_controller.view.*
|
import kotlinx.android.synthetic.main.library_controller.view.*
|
||||||
|
import rx.Subscription
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -118,6 +120,10 @@ class LibraryController(
|
||||||
*/
|
*/
|
||||||
private var drawerListener: DrawerLayout.DrawerListener? = null
|
private var drawerListener: DrawerLayout.DrawerListener? = null
|
||||||
|
|
||||||
|
private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create()
|
||||||
|
|
||||||
|
private var tabsVisibilitySubscription: Subscription? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
@ -173,6 +179,8 @@ class LibraryController(
|
||||||
super.onDestroyView(view)
|
super.onDestroyView(view)
|
||||||
adapter = null
|
adapter = null
|
||||||
actionMode = null
|
actionMode = null
|
||||||
|
tabsVisibilitySubscription?.unsubscribe()
|
||||||
|
tabsVisibilitySubscription = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup {
|
override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup {
|
||||||
|
@ -204,6 +212,27 @@ class LibraryController(
|
||||||
navView = null
|
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>>) {
|
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>) {
|
||||||
val view = view ?: return
|
val view = view ?: return
|
||||||
val adapter = adapter ?: return
|
val adapter = adapter ?: return
|
||||||
|
@ -227,7 +256,7 @@ class LibraryController(
|
||||||
// Restore active category.
|
// Restore active category.
|
||||||
view.view_pager.setCurrentItem(activeCat, false)
|
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.
|
// Delay the scroll position to allow the view to be properly measured.
|
||||||
view.post {
|
view.post {
|
||||||
|
@ -282,13 +311,6 @@ class LibraryController(
|
||||||
adapter.recycle = true
|
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.
|
* 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setAppTheme()
|
setAppTheme()
|
||||||
|
@ -69,6 +69,8 @@ class MainActivity : BaseActivity() {
|
||||||
drawerArrow?.color = Color.WHITE
|
drawerArrow?.color = Color.WHITE
|
||||||
toolbar.navigationIcon = drawerArrow
|
toolbar.navigationIcon = drawerArrow
|
||||||
|
|
||||||
|
tabAnimator = TabsAnimator(tabs)
|
||||||
|
|
||||||
// Set behavior of Navigation drawer
|
// Set behavior of Navigation drawer
|
||||||
nav_view.setNavigationItemSelectedListener { item ->
|
nav_view.setNavigationItemSelectedListener { item ->
|
||||||
val id = item.itemId
|
val id = item.itemId
|
||||||
|
@ -190,8 +192,8 @@ class MainActivity : BaseActivity() {
|
||||||
from.cleanupTabs(tabs)
|
from.cleanupTabs(tabs)
|
||||||
}
|
}
|
||||||
if (to is TabbedController) {
|
if (to is TabbedController) {
|
||||||
to.configureTabs(tabs)
|
|
||||||
tabAnimator.expand()
|
tabAnimator.expand()
|
||||||
|
to.configureTabs(tabs)
|
||||||
} else {
|
} else {
|
||||||
tabAnimator.collapse()
|
tabAnimator.collapse()
|
||||||
tabs.setupWithViewPager(null)
|
tabs.setupWithViewPager(null)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.ui.main
|
package eu.kanade.tachiyomi.ui.main
|
||||||
|
|
||||||
import android.support.design.widget.TabLayout
|
import android.support.design.widget.TabLayout
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
import android.view.animation.Animation
|
import android.view.animation.Animation
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.view.animation.Transformation
|
import android.view.animation.Transformation
|
||||||
|
@ -9,15 +11,32 @@ import eu.kanade.tachiyomi.util.visible
|
||||||
|
|
||||||
class TabsAnimator(val tabs: TabLayout) {
|
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()
|
private val interpolator = DecelerateInterpolator()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the animation.
|
||||||
|
*/
|
||||||
private val duration = 300L
|
private val duration = 300L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation used to expand the tab layout.
|
||||||
|
*/
|
||||||
private val expandAnimation = object : Animation() {
|
private val expandAnimation = object : Animation() {
|
||||||
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||||
tabs.layoutParams.height = (height * interpolatedTime).toInt()
|
setHeight((tabsHeight * interpolatedTime).toInt())
|
||||||
tabs.requestLayout()
|
tabs.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +45,24 @@ class TabsAnimator(val tabs: TabLayout) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation used to collapse the tab layout.
|
||||||
|
*/
|
||||||
private val collapseAnimation = object : Animation() {
|
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) {
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||||
if (interpolatedTime == 1f) {
|
if (interpolatedTime == 0f) {
|
||||||
|
startHeight = tabs.height
|
||||||
|
} else if (interpolatedTime == 1f) {
|
||||||
tabs.gone()
|
tabs.gone()
|
||||||
} else {
|
} else {
|
||||||
tabs.layoutParams.height = (height * (1 - interpolatedTime)).toInt()
|
setHeight((startHeight * (1 - interpolatedTime)).toInt())
|
||||||
tabs.requestLayout()
|
tabs.requestLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,29 +77,75 @@ class TabsAnimator(val tabs: TabLayout) {
|
||||||
collapseAnimation.interpolator = interpolator
|
collapseAnimation.interpolator = interpolator
|
||||||
expandAnimation.duration = duration
|
expandAnimation.duration = duration
|
||||||
expandAnimation.interpolator = interpolator
|
expandAnimation.interpolator = interpolator
|
||||||
}
|
|
||||||
|
|
||||||
fun expand() {
|
isLastStateShown = tabs.visibility == View.VISIBLE
|
||||||
tabs.visible()
|
tabs.viewTreeObserver.addOnGlobalLayoutListener(
|
||||||
if (measure() && tabs.measuredHeight != height) {
|
object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
tabs.startAnimation(expandAnimation)
|
override fun onGlobalLayout() {
|
||||||
}
|
if (tabs.height > 0) {
|
||||||
}
|
tabs.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
|
|
||||||
fun collapse() {
|
// Save the tabs default height.
|
||||||
if (measure() && tabs.measuredHeight != 0) {
|
tabsHeight = tabs.height
|
||||||
tabs.startAnimation(collapseAnimation)
|
|
||||||
} else {
|
// Now that we know the height, set the initial height and visibility.
|
||||||
tabs.gone()
|
if (isLastStateShown) {
|
||||||
}
|
setHeight(tabsHeight)
|
||||||
|
tabs.visible()
|
||||||
|
} 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 {
|
private fun setHeight(newHeight: Int) {
|
||||||
if (height > 0) return true
|
tabs.layoutParams.height = newHeight
|
||||||
height = tabs.measuredHeight
|
|
||||||
return height > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:id="@+id/tabs"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
|
||||||
android:theme="@style/Theme.ActionBar.Tab"
|
android:theme="@style/Theme.ActionBar.Tab"
|
||||||
app:tabIndicatorColor="@android:color/white"
|
app:tabIndicatorColor="@android:color/white"
|
||||||
app:tabGravity="center"
|
app:tabGravity="center"
|
||||||
|
|
Reference in a new issue