You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android开发:如何延迟View隐藏至动画执行完成?TransitionManager动画异常问题排查

解决TransitionManager隐藏View时立即消失的问题

这个问题的核心原因是:当你直接将View设置为View.GONE时,该View会立即从布局树中被移除,TransitionManager还没来得及捕捉它的状态变化来执行消失动画,就只能看到其他布局的位置调整动画,而目标View已经不见了。

下面提供两种可行的解决方案:


方案1:使用AutoTransition(最简单)

AutoTransition是Android提供的默认过渡动画组合,它包含了Fade(淡入淡出)和ChangeBounds(布局位置/尺寸变化)动画。使用它可以自动处理View显示/隐藏时的淡入淡出效果,同时配合布局调整动画,避免View立即消失。

修改你的点击事件代码如下:

binding.btn.setOnClickListener {
    // 替换ChangeBounds为AutoTransition
    val transition = AutoTransition()
    transition.addTarget(binding.ly1)
    transition.addTarget(binding.ly2)
    // 可选:设置动画时长,让效果更明显
    transition.duration = 300

    TransitionManager.beginDelayedTransition(binding.root, transition)
    binding.ly1.visibility = if (binding.ly1.isVisible) View.GONE else View.VISIBLE
}

这样修改后,隐藏ly1时,它会先执行淡出动画,同时ly2平滑上移,动画完成后ly1才会被设置为GONE,完全符合你的需求。


方案2:手动控制View高度+监听动画结束(自定义性更强)

如果你想更精准地控制动画过程(比如只做高度收缩动画,不需要淡入淡出),可以先通过修改View的高度触发ChangeBounds动画,等动画结束后再将View设置为GONE。

步骤说明:

  1. 显示View时:恢复其固定高度并设置为VISIBLE
  2. 隐藏View时:先将高度改为0触发动画,动画结束后再设置为GONE

修改后的完整代码:

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    // 保存ly1的原始高度(35dp)
    private val ly1OriginalHeightDp = 35

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btn.setOnClickListener {
            val transition = ChangeBounds()
            transition.addTarget(binding.ly1)
            transition.addTarget(binding.ly2)
            transition.duration = 300

            if (binding.ly1.isVisible) {
                // 隐藏流程:先添加动画结束监听
                transition.addListener(object : Transition.TransitionListener {
                    override fun onTransitionStart(transition: Transition) {}
                    override fun onTransitionEnd(transition: Transition) {
                        // 动画完成后,将ly1设置为GONE
                        binding.ly1.visibility = View.GONE
                        // 移除监听,避免重复触发
                        transition.removeListener(this)
                    }
                    override fun onTransitionCancel(transition: Transition) {
                        transition.removeListener(this)
                    }
                    override fun onTransitionPause(transition: Transition) {}
                    override fun onTransitionResume(transition: Transition) {}
                })

                TransitionManager.beginDelayedTransition(binding.root, transition)
                // 修改ly1的高度为0,触发收缩动画
                val params = binding.ly1.layoutParams as ConstraintLayout.LayoutParams
                params.height = 0
                binding.ly1.layoutParams = params
            } else {
                // 显示流程:恢复高度并设置为VISIBLE
                TransitionManager.beginDelayedTransition(binding.root, transition)
                binding.ly1.visibility = View.VISIBLE
                val params = binding.ly1.layoutParams as ConstraintLayout.LayoutParams
                params.height = ly1OriginalHeightDp.dpToPx(this)
                binding.ly1.layoutParams = params
            }
        }
    }
}

// 扩展函数:dp转px
fun Int.dpToPx(context: Context): Int {
    return (this * context.resources.displayMetrics.density).toInt()
}

这种方式可以让ly1以高度收缩的动画逐渐消失,同时ly2平滑上移,动画结束后ly1才会被彻底隐藏(从布局树移除)。


内容的提问来源于stack exchange,提问作者dybm27

火山引擎 最新活动