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

能否使用MotionLayout或Transition实现Fragment间共享元素动画?

实现方案

1. 提前初始化Fragment B的ViewPager状态

切换到Fragment B前,必须确保其内部ViewPager已加载并切换到索引1的首页Fragment,否则动画会错位:

  • 在Fragment B的onViewCreated方法中强制ViewPager切换目标页并开启预加载:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val viewPager = view.findViewById<ViewPager2>(R.id.view_pager)
    // 直接切换到索引1的首页Fragment,无滑动动画
    viewPager.setCurrentItem(1, false)
    // 预加载所有4个页面,确保首页Fragment已初始化完成
    viewPager.offscreenPageLimit = 3
}

2. 基础版:固定中心缩放过渡动画

通过自定义动画资源,实现Fragment A全屏缩小退出、Fragment B配合放大进入的效果:

2.1 创建动画资源文件

在res/anim目录下创建scale_out.xml(Fragment A退出动画):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:fromXScale="1.0"
        android:toXScale="0.9"
        android:fromYScale="1.0"
        android:toYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="500"/>
    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0.0"
        android:duration="500"/>
</set>

创建scale_in.xml(Fragment B进入动画):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:fromXScale="0.9"
        android:toXScale="1.0"
        android:fromYScale="0.9"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="500"/>
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="500"/>
</set>

2.2 触发Fragment切换并应用动画

在Fragment A的关闭逻辑中(比如完成按钮点击事件),调用FragmentTransaction设置动画:

btnFinish.setOnClickListener {
    val fragmentB = FragmentB()
    parentFragmentManager.beginTransaction()
        .setCustomAnimations(R.anim.scale_in, R.anim.scale_out)
        .replace(R.id.container, fragmentB)
        .commit()
}

3. 进阶版:精准缩放到首页Fragment位置

如果需要动画精准对齐首页Fragment的布局位置,可通过动态计算缩放原点实现:

3.1 在Fragment B中获取首页Fragment实例

fun getHomeFragment(): Fragment? {
    // ViewPager2的子Fragment标签格式为"f${position}"
    return childFragmentManager.findFragmentByTag("f1")
}

3.2 动态计算并执行缩放动画

btnFinish.setOnClickListener {
    val fragmentB = FragmentB()
    // 先将Fragment B添加到后台并初始化,确保子Fragment已加载
    parentFragmentManager.beginTransaction()
        .add(R.id.container, fragmentB, "fragment_b")
        .hide(fragmentB)
        .commitNow()

    fragmentB.getHomeFragment()?.view?.let { homeView ->
        // 获取首页Fragment视图在屏幕上的中心坐标
        val location = IntArray(2)
        homeView.getLocationOnScreen(location)
        val centerX = location[0] + homeView.width / 2
        val centerY = location[1] + homeView.height / 2

        // 动态创建缩放动画
        val scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(
            requireView(),
            PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 0.9f),
            PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 0.9f),
            PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f)
        )
        scaleAnimator.duration = 500
        scaleAnimator.addListener(object : Animator.AnimatorListener {
            override fun onAnimationEnd(animation: Animator?) {
                // 动画结束后显示Fragment B并移除当前Fragment A
                parentFragmentManager.beginTransaction()
                    .show(fragmentB)
                    .remove(this@FragmentA)
                    .commit()
            }
            override fun onAnimationStart(animation: Animator?) {}
            override fun onAnimationCancel(animation: Animator?) {}
            override fun onAnimationRepeat(animation: Animator?) {}
        })
        // 设置缩放原点为首页Fragment的中心
        requireView().pivotX = centerX.toFloat()
        requireView().pivotY = centerY.toFloat()
        scaleAnimator.start()
    } ?: run {
        // 首页Fragment未加载时, fallback到基础版动画
        parentFragmentManager.beginTransaction()
            .setCustomAnimations(R.anim.scale_in, R.anim.scale_out)
            .replace(R.id.container, fragmentB)
            .remove(this@FragmentA)
            .commit()
    }
}

4. 关键注意事项

  • 调整动画中的缩放比例(如0.9),使其与首页Fragment的实际尺寸匹配,确保视觉衔接自然。
  • 若使用ViewPager而非ViewPager2,子Fragment的标签格式为android:switcher:{containerId}:{position},需对应修改获取逻辑。
  • 动态动画需确保Fragment B已完成视图初始化,否则无法获取首页Fragment的视图坐标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 14:48:24