Android共享元素过渡:阻止共享元素绘制及自定义可见区域动画问题
解决共享元素过渡期间阻止原视图绘制的问题
这个场景我之前也碰到过!在ScrollView嵌套ConstraintLayout的情况下控制共享元素的绘制,核心是要在过渡期间暂时隐藏原视图,同时保证动画结束后恢复正常状态。给你几个靠谱的实现思路:
方案一:利用Alpha属性临时隐藏原视图
在自定义Transition的onCreateAnimator方法中,我们可以在动画启动时将共享元素的alpha设为0(硬件加速下alpha为0的视图会被跳过绘制),动画结束后再恢复为1。这种方式不会改变视图的布局状态,非常安全:
override fun onCreateAnimator( sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues? ): Animator? { val sharedView = endValues?.view ?: return super.onCreateAnimator(sceneRoot, startValues, endValues) // 动画开始时隐藏原视图 sharedView.alpha = 0f // 这里替换成你已经写好的自定义动画逻辑 val customAnimator = ObjectAnimator.ofFloat(sharedView, "translationY", 0f, 200f) customAnimator.duration = 500 // 动画结束后恢复视图可见性 customAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { super.onAnimationEnd(animation) sharedView.alpha = 1f } }) return customAnimator }
方案二:通过Visibility属性控制绘制
如果alpha的方式不符合你的需求,可以尝试直接修改视图的visibility为INVISIBLE(注意不要用GONE,否则会改变布局结构,影响过渡效果),动画结束后恢复原状态:
override fun onCaptureStartValues(values: TransitionValues) { super.onCaptureStartValues(values) // 保存视图原来的可见性状态 values.values["original_visibility"] = values.view.visibility } override fun onCreateAnimator( sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues? ): Animator? { val sharedView = endValues?.view ?: return null val originalVisibility = startValues?.values?.get("original_visibility") as Int // 过渡开始时隐藏原视图 sharedView.visibility = View.INVISIBLE val customAnimator = // 你的自定义动画逻辑 customAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { super.onAnimationEnd(animation) // 恢复原来的可见性 sharedView.visibility = originalVisibility } }) return customAnimator }
方案三:通过SharedElementCallback全局控制
如果你需要对所有共享元素过渡统一处理,可以自定义SharedElementCallback,在过渡的开始和结束回调中控制视图状态:
// 在Activity的onCreate方法中设置 window.sharedElementEnterTransition = YourCustomTransition() window.sharedElementExitTransition = YourCustomTransition() window.sharedElementCallback = object : SharedElementCallback() { override fun onSharedElementStart( sharedElementNames: MutableList<String>?, sharedElements: MutableList<View>?, sharedElementSnapshots: MutableList<View>? ) { super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots) // 过渡开始时隐藏所有共享元素原视图 sharedElements?.forEach { it.alpha = 0f } } override fun onSharedElementEnd( sharedElementNames: MutableList<String>?, sharedElements: MutableList<View>?, sharedElementSnapshots: MutableList<View>? ) { super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots) // 过渡结束后恢复 sharedElements?.forEach { it.alpha = 1f } } }
注意事项
- 不管用哪种方式,一定要确保在动画结束后恢复视图的原始状态,否则会影响页面的正常显示。
- 如果你的自定义动画已经通过
Canvas.clipRect等方式处理了可见区域的裁剪,那么隐藏原视图后,过渡过程中就只会显示你动画处理的可见部分了。
内容的提问来源于stack exchange,提问作者Oscar Méndez
相关产品推荐
相关产品推荐

