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

如何在Navigation Controller中使用共享元素转场

嘿,我刚好在项目里折腾过这个,其实Navigation组件完全支持共享元素转场,只是官方文档的细节有点散,我把完整的实现步骤拆解给你:

实现Navigation组件的共享元素转场

1. 给共享View设置统一的transitionName

首先要保证源Fragment和目标Fragment里的共享View(比如图片、文本框)拥有完全相同的transitionName属性(大小写都要一致!)。

举个例子,源Fragment的布局里:

<ImageView
    android:id="@+id/item_photo"
    android:transitionName="shared_photo"
    android:layout_width="100dp"
    android:layout_height="100dp" />

目标Fragment的对应View也要设置一样的:

<ImageView
    android:id="@+id/detail_photo"
    android:transitionName="shared_photo"
    android:layout_width="match_parent"
    android:layout_height="300dp" />

如果你是用RecyclerView列表跳转,每个item的共享View需要唯一的transitionName(比如"shared_photo_${item.id}"),然后把这个名称通过bundle传到目标Fragment,在目标Fragment里动态设置对应View的transitionName,不然多个item转场会混乱。

2. 在导航图里配置转场动作

打开你的nav_graph.xml,找到跳转对应的<action>标签,添加共享元素转场的相关配置:

<action
    android:id="@+id/action_list_to_detail"
    app:destination="@id/detailFragment"
    <!-- 配置进入和返回的转场动画,这里用系统默认的缩放/位移转场 -->
    app:sharedElementsEnterTransition="@android:transition/changeBounds"
    app:sharedElementsReturnTransition="@android:transition/changeBounds" />

如果需要自定义转场效果,你可以自己创建transition资源文件(比如在res/transition目录下),然后替换这里的系统转场。

3. 触发导航时传递共享元素

这一步是核心,不能直接调用navigate(),要创建FragmentNavigator.Extras对象把共享View传递过去:

Kotlin代码示例:

// 假设是RecyclerView的item点击事件
holder.itemView.item_photo.setOnClickListener {
    val sharedView = it as ImageView
    // 构建Extras,绑定View和它的transitionName
    val extras = FragmentNavigator.Extras.Builder()
        .addSharedElement(sharedView, sharedView.transitionName)
        .build()

    // 带着Extras跳转,同时可以传递参数bundle
    findNavController().navigate(
        R.id.action_list_to_detail,
        bundleOf("photo_id" to item.id), // 可选参数
        null, // NavOptions,比如动画配置
        extras
    )
}

Java代码示例:

holder.itemView.findViewById(R.id.item_photo).setOnClickListener(v -> {
    ImageView sharedView = (ImageView) v;
    FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder()
            .addSharedElement(sharedView, sharedView.getTransitionName())
            .build();

    getNavController().navigate(
            R.id.action_list_to_detail,
            BundleHelper.createPhotoBundle(item.getId()), // 自定义参数bundle
            null,
            extras
    );
});

4. 优化转场体验(可选但推荐)

如果共享View是图片(尤其是网络图片),建议等图片加载完成后再开始转场,避免出现图片还没显示就转场的尴尬:

在目标Fragment的onViewCreated里:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // 先延迟转场
    postponeEnterTransition()
    
    val detailPhoto = view.findViewById<ImageView>(R.id.detail_photo)
    // 用Glide加载图片的话,在加载完成后触发转场
    Glide.with(this)
        .load(getPhotoUrl())
        .listener(object : RequestListener<Drawable> {
            override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
                startPostponedEnterTransition()
                return false
            }

            override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                startPostponedEnterTransition()
                return false
            }
        })
        .into(detailPhoto)
}

常见坑点排查

  • 确保两个View的transitionName完全一致,包括大小写和特殊字符
  • 必须用Navigation组件的navigate()方法跳转,不能手动replace Fragment
  • RecyclerView场景下一定要给每个item的共享View设置唯一的transitionName
  • 如果转场没生效,检查导航图的<action>里是否正确配置了共享元素转场属性

这样一套下来,就能实现流畅的共享元素转场效果了,要是还有细节问题可以随时问我!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:11:31