如何实现Android Toolbar下方View平滑滑入滑出带淡入淡出动画
代码核心问题排查
- 位移逻辑完全反向
要实现从Toolbar下方滑入,初始隐藏状态应该让目标View的translationY = -自身高度(藏在Toolbar下方不可见),滑入动画是把translationY变为0,滑出动画是变回-自身高度。你当前的代码显示时传正高度,会直接把View滑到布局下方可视区域外,自然看不见。
- 位移逻辑完全反向
- 无法二次显示的根本原因
你在隐藏动画结束后把View设为GONE,此时View不会参与布局测量,下次调用显示时取myContainer.height拿到的值是0,动画无位移效果,也无法正常显示。
- 无法二次显示的根本原因
- 卡顿问题来源
- 手动调用
clearAnimation()、removeAllListeners()属于多余操作,ViewPropertyAnimator会自动管理动画生命周期,强制清除反而会导致动画中断、掉帧 - 你用
translationY做动画不会改变View的实际布局占比,下方常驻View不会被推动,若你额外写了调整下方View位置的逻辑,很容易触发布局反复重绘导致卡顿 - View设为GONE后触发显示时,需要先让View完成测量才能拿到正确高度,直接开动画会导致首帧跳变
- 调用参数变量不一致
显示时用myContainer.height传参,隐藏时用pdfSearchContainer.height传参,如果两个变量不是指向同一个目标View,会直接导致位移参数错误。
- 调用参数变量不一致
修复后的实现方案
第一步:初始化配置
在onCreate/onViewCreated中先初始化目标View的状态,提前测量高度:
myContainer.apply { // 先设为INVISIBLE参与测量,不要直接GONE visibility = View.INVISIBLE // 等待测量完成后设置初始位置,没有引入KTX的话可以替换为OnPreDrawListener实现 doOnPreDraw { translationY = -it.height.toFloat() alpha = 0f // 测量完成后再设为GONE,后续可以通过measuredHeight拿到正确高度 visibility = View.GONE } }
第二步:动画逻辑重写
private fun animateContainer(isShow: Boolean) { // 优先取已测量的高度,兜底逻辑防止首次测量未完成 val targetHeight = myContainer.measuredHeight.takeIf { it > 0 } ?: run { myContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) myContainer.measuredHeight } // 显示前先把View设为VISIBLE,避免动画过程中不可见 if (isShow) { myContainer.visibility = View.VISIBLE } // 目标View的滑入/滑出+淡入淡出动画 myContainer.animate() .translationY(if (isShow) 0f else -targetHeight.toFloat()) .alpha(if (isShow) 1f else 0f) .setDuration(300) // 加减速插值器更符合系统动效规范,视觉上更丝滑 .setInterpolator(DecelerateInterpolator()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { if (!isShow) { myContainer.visibility = View.GONE } // 不需要手动清除监听和动画,系统会自动回收资源 } }) // 同步推动下方常驻View的动画,假设下方View变量名为bottomContainer bottomContainer.animate() .translationY(if (isShow) targetHeight.toFloat() else 0f) .setDuration(300) .setInterpolator(DecelerateInterpolator()) }
第三步:调用逻辑
显示容器时调用:
animateContainer(true)
隐藏容器时调用:
animateContainer(false)
可选优化方案
如果不需要自定义动画细节,想要更简单的实现,可以用TransitionManager配合约束布局实现,系统会自动处理目标View的滑入滑出、下方View的位移逻辑,流畅度更高:
// parentContainer为两个View的共同父布局 TransitionManager.beginDelayedTransition(parentContainer, Slide(Gravity.TOP).addTarget(myContainer)) myContainer.visibility = if (isShow) View.VISIBLE else View.GONE
内容的提问来源于stack exchange,提问作者Hector
相关产品推荐
相关产品推荐

