You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Android Toolbar下方View平滑滑入滑出带淡入淡出动画

代码核心问题排查

    1. 位移逻辑完全反向
      要实现从Toolbar下方滑入,初始隐藏状态应该让目标View的translationY = -自身高度(藏在Toolbar下方不可见),滑入动画是把translationY变为0,滑出动画是变回-自身高度。你当前的代码显示时传正高度,会直接把View滑到布局下方可视区域外,自然看不见。
    1. 无法二次显示的根本原因
      你在隐藏动画结束后把View设为GONE,此时View不会参与布局测量,下次调用显示时取myContainer.height拿到的值是0,动画无位移效果,也无法正常显示。
    1. 卡顿问题来源
    • 手动调用clearAnimation()、removeAllListeners()属于多余操作,ViewPropertyAnimator会自动管理动画生命周期,强制清除反而会导致动画中断、掉帧
    • 你用translationY做动画不会改变View的实际布局占比,下方常驻View不会被推动,若你额外写了调整下方View位置的逻辑,很容易触发布局反复重绘导致卡顿
    • View设为GONE后触发显示时,需要先让View完成测量才能拿到正确高度,直接开动画会导致首帧跳变
    1. 调用参数变量不一致
      显示时用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 20:06:03