From 2daa131fb2e60b681f793cdf6c1af64cbbb12634 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Wed, 16 Oct 2019 16:08:07 +0200 Subject: [PATCH] Added layout behavior to dismiss toolbars when scrolling --- .../topjohnwu/magisk/redesign/MainActivity.kt | 7 + .../utils/HideTopViewOnScrollBehavior.kt | 130 ++++++++++++++++++ app/src/main/res/layout/activity_main_md2.xml | 2 + app/src/main/res/layout/fragment_home_md2.xml | 2 +- 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/MainActivity.kt index 884e45e6a..b46f6fa38 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/MainActivity.kt @@ -3,8 +3,11 @@ package com.topjohnwu.magisk.redesign import android.graphics.Insets import android.os.Bundle import android.view.ViewTreeObserver +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.setPadding +import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment +import com.google.android.material.card.MaterialCardView import com.ncapdevi.fragnav.FragNavController import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R @@ -16,6 +19,7 @@ import com.topjohnwu.magisk.ui.log.LogFragment import com.topjohnwu.magisk.ui.module.ModulesFragment import com.topjohnwu.magisk.ui.settings.SettingsFragment import com.topjohnwu.magisk.ui.superuser.SuperuserFragment +import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior import com.topjohnwu.superuser.Shell import org.koin.androidx.viewmodel.ext.android.viewModel import kotlin.reflect.KClass @@ -48,6 +52,9 @@ open class MainActivity : CompatActivity( setSupportActionBar(binding.mainToolbar) + binding.mainToolbarWrapper.updateLayoutParams { + behavior = HideTopViewOnScrollBehavior() + } binding.mainNavigation.setOnNavigationItemSelectedListener { when (it.itemId) { R.id.homeFragment -> Navigation.home() diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt b/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt new file mode 100644 index 000000000..39650e230 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt @@ -0,0 +1,130 @@ +package com.topjohnwu.magisk.utils + +import android.animation.TimeInterpolator +import android.view.View +import android.view.ViewGroup +import android.view.ViewPropertyAnimator +import androidx.annotation.Dimension +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.ViewCompat +import com.google.android.material.animation.AnimationUtils +import com.google.android.material.behavior.HideBottomViewOnScrollBehavior + +class HideTopViewOnScrollBehavior : HideBottomViewOnScrollBehavior() { + + companion object { + private const val STATE_SCROLLED_DOWN = 1 + private const val STATE_SCROLLED_UP = 2 + } + + private var height = 0 + private var currentState = STATE_SCROLLED_UP + private var additionalHiddenOffsetY = 0 + private var currentAnimator: ViewPropertyAnimator? = null + + override fun onLayoutChild( + parent: CoordinatorLayout, + child: V, + layoutDirection: Int + ): Boolean { + val paramsCompat = child.layoutParams as ViewGroup.MarginLayoutParams + height = child.measuredHeight + paramsCompat.topMargin + return super.onLayoutChild(parent, child, layoutDirection) + } + + /** + * Sets an additional offset for the y position used to hide the view. + * + * @param child the child view that is hidden by this behavior + * @param offset the additional offset in pixels that should be added when the view slides away + */ + override fun setAdditionalHiddenOffsetY(child: V, @Dimension offset: Int) { + additionalHiddenOffsetY = offset + + if (currentState == STATE_SCROLLED_DOWN) { + child.translationY = (height + additionalHiddenOffsetY).toFloat() + } + } + + override fun onStartNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: V, + directTargetChild: View, + target: View, + nestedScrollAxes: Int + ) = nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL + + override fun onNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: V, + target: View, + dxConsumed: Int, + dyConsumed: Int, + dxUnconsumed: Int, + dyUnconsumed: Int + ) { + when { + dyConsumed > 0 -> slideUp(child) + dyConsumed < 0 -> slideDown(child) + } + } + + /** + * Perform an animation that will slide the child from it's current position to be totally on the + * screen. + */ + override fun slideDown(child: V) { + if (currentState == STATE_SCROLLED_UP) { + return + } + + currentAnimator?.let { + it.cancel() + child.clearAnimation() + } + + currentState = STATE_SCROLLED_UP + animateChildTo( + child, + 0, + ENTER_ANIMATION_DURATION.toLong(), + AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR + ) + } + + /** + * Perform an animation that will slide the child from it's current position to be totally off the + * screen. + */ + override fun slideUp(child: V) { + if (currentState == STATE_SCROLLED_DOWN) { + return + } + + currentAnimator?.let { + it.cancel() + child.clearAnimation() + } + + currentState = STATE_SCROLLED_DOWN + animateChildTo( + child, + -(height + additionalHiddenOffsetY), + EXIT_ANIMATION_DURATION.toLong(), + AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR + ) + } + + private fun animateChildTo( + child: V, + targetY: Int, + duration: Long, + interpolator: TimeInterpolator + ) = child + .animate() + .translationY(targetY.toFloat()) + .setInterpolator(interpolator) + .setDuration(duration) + .withEndAction { currentAnimator = null } + .let { currentAnimator = it } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_md2.xml b/app/src/main/res/layout/activity_main_md2.xml index 8c1d36e2c..a9873680d 100644 --- a/app/src/main/res/layout/activity_main_md2.xml +++ b/app/src/main/res/layout/activity_main_md2.xml @@ -28,6 +28,7 @@ style="?styleCardElevated" android:layout_width="match_parent" android:layout_height="wrap_content" + android:id="@+id/main_toolbar_wrapper" android:layout_marginStart="@dimen/l1" android:layout_marginTop="@{(int) @dimen/l1 + viewModel.insets.top}" android:layout_marginEnd="@dimen/l1" @@ -55,6 +56,7 @@ style="?styleCardElevated" android:layout_width="match_parent" android:layout_height="wrap_content" + app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior" android:layout_gravity="bottom" android:layout_marginStart="@dimen/l1" android:layout_marginEnd="@dimen/l1" diff --git a/app/src/main/res/layout/fragment_home_md2.xml b/app/src/main/res/layout/fragment_home_md2.xml index 0fceffdd7..97b148e39 100644 --- a/app/src/main/res/layout/fragment_home_md2.xml +++ b/app/src/main/res/layout/fragment_home_md2.xml @@ -22,7 +22,7 @@ android:layout_height="match_parent" android:clipToPadding="false" android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l2}" - android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/margin_fab + (int) @dimen/l1}" + android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}" tools:layout_marginTop="24dp">