From bfe6bc309581fac45f53614abd69eafca1b2bb3f Mon Sep 17 00:00:00 2001 From: RikkaW Date: Mon, 17 Jan 2022 18:13:25 +0800 Subject: [PATCH] Fix bottom nav sometimes not hide correctly Replace homemade animation with StateListAnimator --- .../com/topjohnwu/magisk/ui/MainActivity.kt | 49 ++----- .../ConcealableBottomNavigationView.java | 136 ++++++++++++++++++ app/src/main/res/layout/activity_main_md2.xml | 2 +- app/src/main/res/values/attrs.xml | 4 + 4 files changed, 149 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/widget/ConcealableBottomNavigationView.java diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 28026a779..0ee88bcc9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -11,6 +11,7 @@ import android.view.View import android.view.WindowManager import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.view.forEach +import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.interpolator.view.animation.FastOutLinearInInterpolator import androidx.interpolator.view.animation.LinearOutSlowInInterpolator @@ -97,10 +98,8 @@ class MainActivity : BaseMainActivity() { getScreen(section)?.navigate() - if (savedInstanceState != null) { - if (!isRootFragment) { - requestNavigationHidden() - } + if (!isRootFragment) { + requestNavigationHidden(requiresAnimation = savedInstanceState == null) } } @@ -120,45 +119,13 @@ class MainActivity : BaseMainActivity() { } } - @Suppress("UNCHECKED_CAST") - internal fun requestNavigationHidden(hide: Boolean = true) { + internal fun requestNavigationHidden(hide: Boolean = true, requiresAnimation: Boolean = true) { val bottomView = binding.mainNavigation - - // A copy of HideBottomViewOnScrollBehavior's animation - - fun animateTranslationY( - view: View, targetY: Int, duration: Long, interpolator: TimeInterpolator - ) { - view.tag = view - .animate() - .translationY(targetY.toFloat()) - .setInterpolator(interpolator) - .setDuration(duration) - .setListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - view.tag = null - } - }) - } - - (bottomView.tag as? Animator)?.cancel() - bottomView.clearAnimation() - - if (hide) { - animateTranslationY( - bottomView, - bottomView.measuredHeight, - 175L, - FastOutLinearInInterpolator() - ) + if (requiresAnimation) { + bottomView.isVisible = true + bottomView.isHidden = hide } else { - animateTranslationY( - bottomView, - 0, - 225L, - LinearOutSlowInInterpolator() - ) + bottomView.isGone = hide } } diff --git a/app/src/main/java/com/topjohnwu/magisk/widget/ConcealableBottomNavigationView.java b/app/src/main/java/com/topjohnwu/magisk/widget/ConcealableBottomNavigationView.java new file mode 100644 index 000000000..67ca88d13 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/widget/ConcealableBottomNavigationView.java @@ -0,0 +1,136 @@ +package com.topjohnwu.magisk.widget; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.interpolator.view.animation.FastOutLinearInInterpolator; + +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.topjohnwu.magisk.R; + +public class ConcealableBottomNavigationView extends BottomNavigationView { + + private static final int[] STATE_SET = { + R.attr.state_hidden + }; + + private boolean isHidden; + + public ConcealableBottomNavigationView(@NonNull Context context) { + this(context, null); + } + + public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, R.attr.bottomNavigationStyle); + } + + public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, R.style.Widget_Design_BottomNavigationView); + } + + public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + private void recreateAnimator(int height) { + Animator toHidden = ObjectAnimator.ofFloat(this, "translationY", height); + toHidden.setDuration(175); + toHidden.setInterpolator(new FastOutLinearInInterpolator()); + Animator toUnhidden = ObjectAnimator.ofFloat(this, "translationY", 0); + toHidden.setDuration(225); + toHidden.setInterpolator(new FastOutLinearInInterpolator()); + + StateListAnimator animator = new StateListAnimator(); + + animator.addState(STATE_SET, toHidden); + animator.addState(new int[]{}, toUnhidden); + + setStateListAnimator(animator); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + recreateAnimator(getMeasuredHeight()); + } + + @Override + protected int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isHidden()) { + mergeDrawableStates(drawableState, STATE_SET); + } + return drawableState; + } + + public boolean isHidden() { + return isHidden; + } + + public void setHidden(boolean raised) { + if (isHidden != raised) { + isHidden = raised; + refreshDrawableState(); + } + } + + @NonNull + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + state.isHidden = isHidden(); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + final SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + + if (ss.isHidden) { + setHidden(isHidden); + } + } + + static class SavedState extends View.BaseSavedState { + + public boolean isHidden; + + public SavedState(Parcel source) { + super(source); + isHidden = source.readByte() != 0; + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeByte(isHidden ? (byte) 1 : (byte) 0); + } + + public static final Creator CREATOR = new Creator() { + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/app/src/main/res/layout/activity_main_md2.xml b/app/src/main/res/layout/activity_main_md2.xml index c76f08bce..a68be6f2d 100644 --- a/app/src/main/res/layout/activity_main_md2.xml +++ b/app/src/main/res/layout/activity_main_md2.xml @@ -48,7 +48,7 @@ - + + + +