如何实现Fragment与子Fragment及跨层级Fragment的共享元素过渡
共享元素过渡失效问题及跨层级过渡实现方案
问题背景
现有Splash Fragment、带底部导航和bottom_nav_graph的Main Fragment,需实现两个需求:
- Splash到Main默认子Fragment(Home Fragment)的共享元素过渡(过渡元素仅存在于Splash和Home中)
- Home Fragment到与Main同层级的UserDetail Fragment的类似共享元素过渡
当前Splash到Home的过渡未生效,相关代码如下:
SplashFragment 布局代码
<com.airbnb.lottie.LottieAnimationView android:id="@+id/lottie_loading" android:layout_width="192dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:lottie_autoPlay="true" android:transitionName="@string/splash_lottie" app:lottie_fileName="splash.json" />
SplashFragment 逻辑代码
val sharedElement = binding.lottieLoading val extras = FragmentNavigatorExtras(sharedElement to sharedElement.transitionName) findNavController().navigateSafe(R.id.mainContainerFragment, navExtras = extras)
MainFragment 尝试过的代码
要么无额外代码,要么:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) }
HomeFragment 布局代码
<androidx.appcompat.widget.AppCompatImageView android:id="@+id/iv_top_logo" android:layout_width="110dp" android:layout_height="33dp" android:layout_marginTop="40dp" android:scaleType="centerCrop" android:scaleX="1.4" android:scaleY="1.4" android:transitionName="@string/splash_lottie" app:layout_constraintStart_toStartOf="@id/actv_top_label_title" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/logo_white" app:tint="@color/primary" />
HomeFragment 尝试过的逻辑代码
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) }
我认为不同View类型(LottieAnimationView与ImageView)不影响过渡,transitionName是关键,但当前过渡未生效,请问遗漏了什么?另外,Home到同层级UserDetail的过渡该怎么实现?
问题1:Splash到Home共享元素过渡失效的修复
核心遗漏点
从Splash跳转到MainFragment时,共享元素参数默认只会传递给目标Fragment(Main),不会自动转发给它的子Fragment(Home),导致Home无法接收到过渡信号。
修复步骤
MainFragment转发共享元素参数
在MainFragment中获取Splash传来的过渡参数,加载HomeFragment时传递给它:override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 获取从Splash传递的共享元素参数 val navExtras = arguments?.getParcelable<FragmentNavigatorExtras>(NavController.KEY_NAV_EXTRAS) navExtras?.let { extras -> val (sharedView, transitionName) = extras.sharedElements.entries.first() // 加载HomeFragment并传递共享元素 childFragmentManager.beginTransaction() .setReorderingAllowed(true) .addSharedElement(sharedView, transitionName) .replace(R.id.bottom_nav_container, HomeFragment()) .commit() } }禁用MainFragment的过渡动画
Main作为中间容器不需要过渡,避免干扰Splash到Home的动画:override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enterTransition = null exitTransition = null sharedElementEnterTransition = null sharedElementExitTransition = null }确保HomeFragment过渡时机正确
延迟过渡直到目标View加载完成,避免因View未初始化导致过渡失效:override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) postponeEnterTransition() binding.ivTopLogo.doOnPreDraw { startPostponedEnterTransition() } }
问题2:Home到同层级UserDetail Fragment的过渡实现
由于Home和UserDetail同属Main的子Fragment,可直接通过子导航控制器传递共享元素参数:
实现步骤
给UserDetailFragment的过渡View设置相同
transitionName<ImageView android:id="@+id/user_logo" android:transitionName="@string/splash_lottie" <!-- 其他布局属性 --> />HomeFragment中触发跳转并传递参数
val sharedView = binding.ivTopLogo val extras = FragmentNavigatorExtras(sharedView to sharedView.transitionName) findNavController().navigate(R.id.action_home_to_userDetail, null, null, extras)配置UserDetailFragment的过渡
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) postponeEnterTransition() binding.userLogo.doOnPreDraw { startPostponedEnterTransition() } }
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

