Fragment中结合AsyncLayoutInflater与SharedElementTransition实现遇阻求助
问题分析与解决方案
你的代码无法正常工作主要有几个核心问题,咱们一步步拆解:
1. AsyncLayoutInflater的回调运行在后台线程
AsyncLayoutInflater的核心是在后台线程完成布局inflation,但你在回调里直接执行了addView、设置transitionName这些UI操作——Android要求所有UI操作必须在主线程执行,这不仅会导致线程安全问题,还会让共享元素过渡系统无法正确识别目标视图(因为视图是在后台线程被添加的,主线程的视图树还没同步)。
2. 启动过渡的时机过早
startPostponedEnterTransition()被直接放在回调里,但此时视图可能还没被完全附加到视图树、没完成测量/layout,过渡系统找不到有效的共享元素,自然无法触发过渡动画。
修正后的代码实现
下面是修复后的完整逻辑,我会标注关键改动点:
// 首先,在Fragment的onCreateView里先推迟过渡 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // 必须先调用这个,告诉系统等待我们准备好再启动过渡 postponeEnterTransition() // 创建AsyncLayoutInflater实例 val asyncLayoutInflater = AsyncLayoutInflater(requireContext()) // 这里用FrameLayout作为根容器,也可以复用你原本的view参数 val rootView = FrameLayout(requireContext()) asyncLayoutInflater.inflate(R.layout.fragment, rootView) { inflatedView, _, _ -> // 1. 所有UI操作切换到主线程执行 requireActivity().runOnUiThread { val binding = FragmentBinding.bind(inflatedView) binding.viewModel = viewModel // 确保视图只被添加一次,避免重复添加的异常 if (inflatedView.parent == null) { rootView.addView(inflatedView) } // 2. 给共享元素设置transitionName if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { binding.myImageView.transitionName = entity.name } // 3. 监听视图绘制完成,确保共享元素已准备好再启动过渡 binding.myImageView.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { binding.myImageView.viewTreeObserver.removeOnPreDrawListener(this) // 现在可以安全启动过渡了 startPostponedEnterTransition() displayInfo() return true } }) } } return rootView }
额外注意事项
- 确保启动过渡的当前Fragment和发起过渡的源Fragment都正确配置了共享元素:源Fragment的ImageView也要设置相同的
transitionName,并且在跳转时通过FragmentNavigatorExtras传递共享元素信息。 - 如果你的
view参数是来自onCreateView的container,注意先判断inflatedView.parent == null再执行addView,避免视图重复添加的崩溃。 - AsyncLayoutInflater只负责后台inflation操作,所有后续的视图绑定、添加到视图树、过渡配置等逻辑,都必须回到主线程执行。
内容的提问来源于stack exchange,提问作者Ruslan Leshchenko
相关产品推荐
相关产品推荐

