Better BottomNavigationView hide/unhide animation

This commit is contained in:
Prathamesh More 2022-05-03 20:31:53 +05:30
parent e7f700ee9a
commit 95e39e4f4c
2 changed files with 87 additions and 8 deletions

View file

@ -366,18 +366,15 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
)
return
}
val translationY =
if (visible) 0F else dip(R.dimen.bottom_nav_height).toFloat() + windowInsets.safeGetBottomInsets()
val mAnimate = animate && bottomSheetBehavior.state == STATE_COLLAPSED
if (mAnimate) {
binding.bottomNavigationView.translateYAnimate(translationY).doOnEnd {
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
binding.bottomNavigationView.bringToFront()
}
if (visible) {
binding.bottomNavigationView.bringToFront()
binding.bottomNavigationView.show()
} else {
binding.bottomNavigationView.hide()
}
} else {
binding.bottomNavigationView.translationY =
translationY
binding.bottomNavigationView.isVisible = false
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
binding.bottomNavigationView.bringToFront()

View file

@ -16,10 +16,13 @@ package code.name.monkey.retromusic.extensions
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.animation.AnimationUtils
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.LayoutRes
@ -32,6 +35,7 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import dev.chrisbanes.insetter.applyInsetter
@ -58,6 +62,84 @@ fun EditText.appHandleColor(): EditText {
return this
}
/**
* Potentially animate showing a [BottomNavigationView].
*
* Abruptly changing the visibility leads to a re-layout of main content, animating
* `translationY` leaves a gap where the view was that content does not fill.
*
* Instead, take a snapshot of the view, and animate this in, only changing the visibility (and
* thus layout) when the animation completes.
*/
fun BottomNavigationView.show() {
if (isVisible) return
val parent = parent as ViewGroup
// View needs to be laid out to create a snapshot & know position to animate. If view isn't
// laid out yet, need to do this manually.
if (!isLaidOut) {
measure(
View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.AT_MOST)
)
layout(parent.left, parent.height - measuredHeight, parent.right, parent.height)
}
val drawable = BitmapDrawable(context.resources, drawToBitmap())
drawable.setBounds(left, parent.height, right, parent.height + height)
parent.overlay.add(drawable)
ValueAnimator.ofInt(parent.height, top).apply {
duration = 300
interpolator = AnimationUtils.loadInterpolator(
context,
android.R.interpolator.linear_out_slow_in
)
addUpdateListener {
val newTop = it.animatedValue as Int
drawable.setBounds(left, newTop, right, newTop + height)
}
doOnEnd {
parent.overlay.remove(drawable)
isVisible = true
}
start()
}
}
/**
* Potentially animate hiding a [BottomNavigationView].
*
* Abruptly changing the visibility leads to a re-layout of main content, animating
* `translationY` leaves a gap where the view was that content does not fill.
*
* Instead, take a snapshot, instantly hide the view (so content lays out to fill), then animate
* out the snapshot.
*/
fun BottomNavigationView.hide() {
if (isGone) return
val drawable = BitmapDrawable(context.resources, drawToBitmap())
val parent = parent as ViewGroup
drawable.setBounds(left, top, right, bottom)
parent.overlay.add(drawable)
isGone = true
ValueAnimator.ofInt(top, parent.height).apply {
duration = 300L
interpolator = AnimationUtils.loadInterpolator(
context,
android.R.interpolator.fast_out_linear_in
)
addUpdateListener {
val newTop = it.animatedValue as Int
drawable.setBounds(left, newTop, right, newTop + height)
}
doOnEnd {
parent.overlay.remove(drawable)
}
start()
}
}
fun View.translateYAnimate(value: Float): Animator {
return ObjectAnimator.ofFloat(this, "translationY", value)
.apply {