Updated navigation behavior to be consistent and easily manageable

This commit is contained in:
Viktor De Pasquale 2020-01-04 13:30:21 +01:00
parent 67b5f39df2
commit b8b0f257db
4 changed files with 92 additions and 42 deletions

View File

@ -3,14 +3,12 @@ package com.topjohnwu.magisk.redesign
import android.graphics.Insets import android.graphics.Insets
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.ViewGroup import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isVisible
import androidx.core.view.setPadding import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavController
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
@ -25,6 +23,7 @@ import com.topjohnwu.magisk.redesign.module.ModuleFragment
import com.topjohnwu.magisk.redesign.superuser.SuperuserFragment import com.topjohnwu.magisk.redesign.superuser.SuperuserFragment
import com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior import com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideableBehavior
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -84,10 +83,9 @@ open class MainActivity : CompatActivity<MainViewModel, ActivityMainMd2Binding>(
if (savedInstanceState != null) { if (savedInstanceState != null) {
onTabTransaction(null, -1) onTabTransaction(null, -1)
onFragmentTransaction(null, FragNavController.TransactionType.PUSH)
if (!navigation.isRoot) { if (!navigation.isRoot) {
requestNavigationHidden(animate = false) requestNavigationHidden()
} }
} }
} }
@ -114,19 +112,13 @@ open class MainActivity : CompatActivity<MainViewModel, ActivityMainMd2Binding>(
return true return true
} }
override fun onTabTransaction(fragment: Fragment?, index: Int) { override fun onTabTransaction(fragment: Fragment?, index: Int) =
setDisplayHomeAsUpEnabled(false) onFragmentTransaction(fragment, FragNavController.TransactionType.PUSH)
}
override fun onFragmentTransaction( override fun onFragmentTransaction(
fragment: Fragment?, fragment: Fragment?,
transactionType: FragNavController.TransactionType transactionType: FragNavController.TransactionType
) { ) {
binding.mainToolbarWrapper.animate()
.translationY(0f)
.setInterpolator(FastOutSlowInInterpolator())
.start()
setDisplayHomeAsUpEnabled(!navigation.isRoot) setDisplayHomeAsUpEnabled(!navigation.isRoot)
requestNavigationHidden(!navigation.isRoot) requestNavigationHidden(!navigation.isRoot)
} }
@ -143,25 +135,19 @@ open class MainActivity : CompatActivity<MainViewModel, ActivityMainMd2Binding>(
} }
} }
internal fun requestNavigationHidden(hide: Boolean = true, animate: Boolean = true) { @Suppress("UNCHECKED_CAST")
val lapam = binding.mainBottomBar.layoutParams as ViewGroup.MarginLayoutParams internal fun requestNavigationHidden(hide: Boolean = true) {
val height = binding.mainBottomBar.measuredHeight val topView = binding.mainToolbarWrapper
val verticalMargin = lapam.let { it.topMargin + it.bottomMargin } val bottomView = binding.mainBottomBar
val maxTranslation = height + verticalMargin
val translation = if (!hide) 0 else maxTranslation
if (!animate) { val topParams = topView.layoutParams as? CoordinatorLayout.LayoutParams
binding.mainBottomBar.translationY = translation.toFloat() val bottomParams = bottomView.layoutParams as? CoordinatorLayout.LayoutParams
binding.mainBottomBar.isVisible = !hide
return
}
binding.mainBottomBar.animate() val topBehavior = topParams?.behavior as? HideableBehavior<View>
.translationY(translation.toFloat()) val bottomBehavior = bottomParams?.behavior as? HideableBehavior<View>
.setInterpolator(FastOutSlowInInterpolator())
.withStartAction { if (!hide) binding.mainBottomBar.isVisible = true } topBehavior?.setHidden(topView, hide = false, lockState = false)
.withEndAction { if (hide) binding.mainBottomBar.isVisible = false } bottomBehavior?.setHidden(bottomView, hide, hide)
.start()
} }
fun invalidateToolbar() { fun invalidateToolbar() {

View File

@ -11,14 +11,17 @@ import com.google.android.material.snackbar.Snackbar
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import kotlin.math.roundToInt import kotlin.math.roundToInt
class HideBottomViewOnScrollBehavior<T : View> : HideBottomViewOnScrollBehavior<T>() { class HideBottomViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(),
HideableBehavior<V> {
override fun layoutDependsOn(parent: CoordinatorLayout, child: T, dependency: View) = private var lockState: Boolean = false
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View) =
super.layoutDependsOn(parent, child, dependency) or (dependency is Snackbar.SnackbarLayout) super.layoutDependsOn(parent, child, dependency) or (dependency is Snackbar.SnackbarLayout)
override fun onDependentViewChanged( override fun onDependentViewChanged(
parent: CoordinatorLayout, parent: CoordinatorLayout,
child: T, child: V,
dependency: View dependency: View
) = when (dependency) { ) = when (dependency) {
is Snackbar.SnackbarLayout -> onDependentViewChanged(parent, child, dependency) is Snackbar.SnackbarLayout -> onDependentViewChanged(parent, child, dependency)
@ -27,7 +30,7 @@ class HideBottomViewOnScrollBehavior<T : View> : HideBottomViewOnScrollBehavior<
override fun onDependentViewRemoved( override fun onDependentViewRemoved(
parent: CoordinatorLayout, parent: CoordinatorLayout,
child: T, child: V,
dependency: View dependency: View
) = when (dependency) { ) = when (dependency) {
is Snackbar.SnackbarLayout -> onDependentViewRemoved(parent, child, dependency) is Snackbar.SnackbarLayout -> onDependentViewRemoved(parent, child, dependency)
@ -38,7 +41,7 @@ class HideBottomViewOnScrollBehavior<T : View> : HideBottomViewOnScrollBehavior<
private fun onDependentViewChanged( private fun onDependentViewChanged(
parent: CoordinatorLayout, parent: CoordinatorLayout,
child: T, child: V,
dependency: Snackbar.SnackbarLayout dependency: Snackbar.SnackbarLayout
): Boolean { ): Boolean {
val viewMargin = (child.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin val viewMargin = (child.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin
@ -58,7 +61,7 @@ class HideBottomViewOnScrollBehavior<T : View> : HideBottomViewOnScrollBehavior<
private fun onDependentViewRemoved( private fun onDependentViewRemoved(
parent: CoordinatorLayout, parent: CoordinatorLayout,
child: T, child: V,
dependency: Snackbar.SnackbarLayout dependency: Snackbar.SnackbarLayout
) { ) {
// checks whether the navigation is not hidden via scroll // checks whether the navigation is not hidden via scroll
@ -69,6 +72,36 @@ class HideBottomViewOnScrollBehavior<T : View> : HideBottomViewOnScrollBehavior<
//--- //---
override fun slideUp(child: V) {
if (lockState) return
super.slideUp(child)
}
override fun slideDown(child: V) {
if (lockState) return
super.slideDown(child)
}
override fun setHidden(
view: V,
hide: Boolean,
lockState: Boolean
) {
if (!lockState) {
this.lockState = lockState
}
if (hide) {
slideDown(view)
} else {
slideUp(view)
}
this.lockState = lockState
}
//---
private fun View.translationY(destination: Float) = animate() private fun View.translationY(destination: Float) = animate()
.translationY(destination) .translationY(destination)
.setInterpolator(FastOutSlowInInterpolator()) .setInterpolator(FastOutSlowInInterpolator())

View File

@ -10,7 +10,8 @@ import androidx.core.view.ViewCompat
import com.google.android.material.animation.AnimationUtils import com.google.android.material.animation.AnimationUtils
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior import com.google.android.material.behavior.HideBottomViewOnScrollBehavior
class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>() { class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(),
HideableBehavior<V> {
companion object { companion object {
private const val STATE_SCROLLED_DOWN = 1 private const val STATE_SCROLLED_DOWN = 1
@ -21,6 +22,7 @@ class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(
private var currentState = STATE_SCROLLED_UP private var currentState = STATE_SCROLLED_UP
private var additionalHiddenOffsetY = 0 private var additionalHiddenOffsetY = 0
private var currentAnimator: ViewPropertyAnimator? = null private var currentAnimator: ViewPropertyAnimator? = null
private var lockState: Boolean = false
override fun onLayoutChild( override fun onLayoutChild(
parent: CoordinatorLayout, parent: CoordinatorLayout,
@ -63,10 +65,30 @@ class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(
dxUnconsumed: Int, dxUnconsumed: Int,
dyUnconsumed: Int dyUnconsumed: Int
) { ) {
when { // when initiating scroll while the view is at the bottom or at the top and pushing it
dyConsumed > 0 -> slideUp(child) // further, the parent will report consumption of 0
dyConsumed < 0 -> slideDown(child) if (dyConsumed == 0) return
setHidden(child, dyConsumed > 0, false)
}
@Suppress("UNCHECKED_CAST")
override fun setHidden(
view: V,
hide: Boolean,
lockState: Boolean
) {
if (!lockState) {
this.lockState = lockState
} }
if (hide) {
slideUp(view)
} else {
slideDown(view)
}
this.lockState = lockState
} }
/** /**
@ -74,7 +96,7 @@ class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(
* screen. * screen.
*/ */
override fun slideDown(child: V) { override fun slideDown(child: V) {
if (currentState == STATE_SCROLLED_UP) { if (currentState == STATE_SCROLLED_UP || lockState) {
return return
} }
@ -97,7 +119,7 @@ class HideTopViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(
* screen. * screen.
*/ */
override fun slideUp(child: V) { override fun slideUp(child: V) {
if (currentState == STATE_SCROLLED_DOWN) { if (currentState == STATE_SCROLLED_DOWN || lockState) {
return return
} }

View File

@ -0,0 +1,9 @@
package com.topjohnwu.magisk.utils
import android.view.View
interface HideableBehavior<V : View> {
fun setHidden(view: V, hide: Boolean, lockState: Boolean = false)
}