Android Transition转场动效使用全解析

发布于:2025-04-11 ⋅ 阅读:(40) ⋅ 点赞:(0)

Transition的使用和原理

项目效果

1. 简述

Android 4.4.2 中引入了 Transition 过渡动画,不过功能比较简单。在 Android 5.0 的 Material Design 中引入更完整和强大的 Transition 框架。通过Transition可以实现:

  • 同一个页面中的场景过渡动画
  • Activity/Fragment 切换时的内容过渡动画(content transition)
  • Activity/Fragment 切换时的共享元素过渡动画

核心作用:降低动效的开发成本

2. 关键概念和类

  • **Scene:**场景,定义了一个确定的 UI 状态

场景表示视图层级结构中各种属性在应用该场景时所具有的值的集合。一个场景可以被配置为在应用时自动运行一个过渡动画,该过渡动画会对在场景变化过程中发生的各种属性变化进行动画处理。

创建Scene有两种方式:

  1. Scene.getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
  2. new Scene(ViewGroup sceneRoot, View layout)
  • **Transition:**转换,定义了两个场景切换时的动画。
    Transition是一个抽象类,系统提供了一些实现类,也可以通过继承Transition来自定义Transition)

当两个场景切换时,Transition 主要有下面两个行为:

  1. 确定开始场景和结束场景中每个 view 的状态。
  2. 根据状态差异创建 Animator,用以场景切换时 view 执行的动画。
  • **TransitionManager:**用于管理场景之间的过渡。有以下两种方式进行场景切换
  1. TransitionManager.go(Scene scene, Transition transition)
  2. TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition)

3. 系统提供的Transition

系统提供的Transition通常包括淡入/淡出、滑动、缩放等动画效果。
在这里插入图片描述

4. 自定义Transition

自定义Transition步骤和示例如下

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.drawable.Drawable;
import android.transition.Transition;
import android.transition.TransitionValues;
import android.view.ViewGroup;
import android.widget.ImageView;

/**
 * 自定义Transition,实现两个不相同的图片的转场
 */
//1.继承Transition
public class ChangeImageResourceTransition extends Transition {
    //2.属性定义:官方建议命名规则:package_name:transition_class:property_name
    private static final String CHANGE_IMAGE = "plato:ChangeImageResourceTransition:src";
    //3.采集起始帧(属性)
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }
    //4.采集终止帧(属性)
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }
    private void captureValues(TransitionValues transitionValues) {
        if (transitionValues == null || !(transitionValues.view instanceof ImageView)) return;
        ImageView imageView = (ImageView) transitionValues.view;
        transitionValues.values.put(CHANGE_IMAGE, imageView.getDrawable());
    }
    //5.根据起始属性和终止属性创建属性动画
    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null )
            return null;
        if (endValues.view instanceof ImageView){
            return super.createAnimator(sceneRoot, startValues, endValues);
        }
        ImageView endView = (ImageView) endValues.view;
        Drawable startDrawable = (Drawable) startValues.values.get(CHANGE_IMAGE);
        Drawable endDrawable = (Drawable) endValues.values.get(CHANGE_IMAGE);
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1f);
        animator.addUpdateListener(animation -> {
            float animatedValue = (float) animation.getAnimatedValue();
            if (animatedValue <= 0.5) {
                endView.setImageDrawable(startDrawable);
                float ratio = (0.5f - animatedValue) / 0.5f;
                endView.setAlpha(ratio);
            } else {
                endView.setImageDrawable(endDrawable);
                float ratio = (animatedValue - 0.5f) / 0.5f;
                endView.setAlpha(ratio);
            }
        });
        animator.setDuration(2000);
        return animator;
    }
}

如上定义了一个Transition,这个Transition用来作用于两个不同的图片之间的转换,使之更加平滑自然。关键点在于0-0.5-1之间的变化

5. Transition目标管理

ViewGroup中可能有多个子view,有时我们希望只针对指定的view添加转换效果,此时可以为transition指定目标view:

  • 增加动画目标:
    addTarget(View target)
    addTarget(int targetViewId)
    addTarget(String targetName)
    addTarget(Class targetType)
  • 移除动画目标:
    removeTarget(View target)
    removeTarget(int targetId)
    removeTarget(String targetName)
    removeTarget(Class target)
  • 排除不进行动画的view:
    excludeTarget(View target, boolean exclude)
    excludeTarget(int targetId, boolean exclude)
    excludeTarget(Class type, boolean exclude)
    excludeTarget(Class type, boolean exclude)
  • 排除某个 ViewGroup 的所有子View:
    excludeChildren(View target, boolean exclude)
    excludeChildren(int targetId, boolean exclude)
    excludeChildren(Class type, boolean exclude)

除了通过代码形式,也可以在xml中进行配置。

6.Transition的创建

方式一:XML定义(res/transition下)

<?xml version="1.0" encoding="utf-8"?><!-- res/transition/slide_transition.xml -->
<slide  xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:slideEdge="top" />

TransitionSet也是Transition的子类,可以对多个Transition进行组合

<?xml version="1.0" encoding="utf-8"?><!-- res/transition/shared_image.xml -->
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:transitionOrdering="together">
    <changeClipBounds />
    <changeTransform />
    <changeBounds />
    <changeImageTransform />
</transitionSet>

方式二:代码创建

        val slide = Slide().apply {
            duration = 500
            interpolator = AccelerateDecelerateInterpolator()
            slideEdge = Gravity.TOP
        }
		
		val enterTransitionSet = TransitionSet().apply {
            addTransition(ChangeClipBounds())
            addTransition(ChangeTransform())
            addTransition(ChangeBounds())
            addTransition(ChangeImageTransform())
            if (!cartoon.hasObtain) {//可以根据条件添加
                addTransition(CustomTransition())
            }
        }

7.布局元素动画

在单个页面内使用Transition有两种方式:

  • TransitionManager.go(Scene scene, Transition transition)
    layout_scene1.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f00">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="@drawable/boy"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:text="name"
        android:textColor="#fff"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="@id/iv_icon"
        app:layout_constraintStart_toEndOf="@id/iv_icon"
        app:layout_constraintTop_toTopOf="@id/iv_icon" />

</androidx.constraintlayout.widget.ConstraintLayout>

layout_scene2.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#062FF8">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="centerCrop"
        android:src="@drawable/boy"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="name"
        android:textColor="#FFCA28"
        android:textSize="50sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="@id/iv_icon"
        app:layout_constraintStart_toStartOf="@id/iv_icon"
        app:layout_constraintTop_toBottomOf="@id/iv_icon" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_transition_go.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal">

    <FrameLayout
        android:id="@+id/sceneRoot"
        android:layout_width="1000dp"
        android:layout_height="500dp"
        android:background="@color/color7"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btClick"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onOneClick"
        android:text="场景一"
        android:textSize="100sp"
        app:layout_constraintStart_toStartOf="@id/sceneRoot"
        app:layout_constraintTop_toBottomOf="@id/sceneRoot" />

    <Button
        android:id="@+id/btClick2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onTwoClick"
        android:text="场景二"
        android:textSize="100sp"
        app:layout_constraintEnd_toEndOf="@id/sceneRoot"
        app:layout_constraintTop_toBottomOf="@id/sceneRoot" />
</androidx.constraintlayout.widget.ConstraintLayout>

class TransitionGoActivity : AppCompatActivity() {

    private var mRoot: FrameLayout? = null
    private var mScene1: Scene? = null//场景1
    private var mScene2: Scene? = null//场景2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_transition_go)
        mRoot = findViewById(R.id.sceneRoot)
        mScene1 = Scene.getSceneForLayout(mRoot!!, R.layout.layout_scene1, this)
        mScene2 = Scene.getSceneForLayout(mRoot!!, R.layout.layout_scene2, this)
    }

    fun onOneClick(view: View?) {
        TransitionManager.go(mScene1!!)
    }

    fun onTwoClick(view: View?) {
        TransitionManager.go(mScene2!!)
    }
}

以上代码实现点击两个不同的button,切到不同的Scene中。效果如下:
实际效果

  • TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition)
    activity_transition_begin_delay.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/scene_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.TransitionBeginDelayActivity">

    <TextView
        android:id="@+id/tv_transition1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Hello"
        android:textSize="@dimen/text_size"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_transition2"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:gravity="center"
        android:src="@drawable/girl"
        android:tag="girl"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_transition1" />

    <Button
        android:id="@+id/btn_slide"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="slide"
        android:textAllCaps="false"
        android:textSize="30sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/iv_transition2" />

    <Button
        android:id="@+id/btn_custom_transition"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="customTransition"
        android:textAllCaps="false"
        android:textSize="30sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn_slide" />

</androidx.constraintlayout.widget.ConstraintLayout>
import android.os.Bundle
import android.text.TextUtils
import android.transition.Slide
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplication.R
import com.example.myapplication.transition.ChangeImageResourceTransition

class TransitionBeginDelayActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_transition_begin_delay)
        initListener()
    }

    private fun initListener() {
        //改变文本是否可见
        findViewById<View>(R.id.btn_slide).setOnClickListener {
            val sceneRoot1 = findViewById<ViewGroup>(R.id.scene_root)
            val tvTransition1 = findViewById<View>(R.id.tv_transition1)
            val slide = Slide(Gravity.END)
            val transitionSet = TransitionSet()
            transitionSet.addTransition(slide)
            TransitionManager.beginDelayedTransition(sceneRoot1, transitionSet)
            if (tvTransition1.visibility == View.VISIBLE) {
                tvTransition1.visibility = View.INVISIBLE
            } else {
                tvTransition1.visibility = View.VISIBLE
            }
        }

        //改变图片的内容
        findViewById<View>(R.id.btn_custom_transition).setOnClickListener {
            val changeImageResourceTransition = ChangeImageResourceTransition()
            val sceneRoot2 = findViewById<ViewGroup>(R.id.scene_root)
            val ivTransition = findViewById<ImageView>(R.id.iv_transition2)
            changeImageResourceTransition.addTarget(ivTransition)
            TransitionManager.beginDelayedTransition(sceneRoot2, changeImageResourceTransition)
            val tag = ivTransition.tag as String
            if (TextUtils.equals(tag, "girl")) {
                ivTransition.setImageResource(R.drawable.boy)
                ivTransition.tag = "boy"
            } else {
                ivTransition.setImageResource(R.drawable.girl)
                ivTransition.tag = "girl"
            }
        }
    }
}

以上代码实现点击两个不同的button,分别文本和图片的属性,进而触发transition动效。效果如下:
实际效果

8.Content Transition

Content Transition用来实现两个不同的页面切换时的转场效果。可以通过Theme或代码设置四种不同的Transition(exitTransition,enterTransition,returnTransition,reenterTransition)及监听。

theme:

    <style name="BaseAppTheme" parent="Theme.AppCompat.Light">
        <item name="android:windowEnterTransition">@transition/slide_transition</item>
        <item name="android:windowExitTransition">@transition/slide_transition</item>
    </style>

代码:

window.exitTransition = slide
window.enterTransition = slide

https://img2.baidu.com/it/u=3246309829,2615376531&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=375

activity_content_transition_start.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.ContentTransitionStartActivity">

    <TextView
        android:id="@+id/tv_start_one1"
        android:layout_width="500dp"
        android:layout_height="500dp"
        android:layout_marginTop="40dp"
        android:background="#F00"
        android:gravity="center"
        android:text="slide"
        android:textAllCaps="false"
        android:textColor="#fff"
        android:textSize="@dimen/text_size"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
class ContentTransitionStartActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_content_transition_start)
        initListener()
    }

    private fun initListener() {
        findViewById<View>(R.id.tv_start_one1).setOnClickListener {
            val slide = Slide()
            //slide.addTarget(R.id.tv_start_one1)//如果不加target,就是对Window的视图进行转场了
            slide.duration = 1000
            window.exitTransition = slide
            val activityOptions =
                ActivityOptions.makeSceneTransitionAnimation(this@ContentTransitionStartActivity)
            startActivity(
                Intent(
                    this@ContentTransitionStartActivity,
                    ContentTransitionEndActivity::class.java
                ), activityOptions.toBundle()
            )
        }
    }
}
class ContentTransitionEndActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_content_transition_end)
        window.allowEnterTransitionOverlap = false
        initListener()
    }

    private fun initListener() {
        findViewById<View>(R.id.tv_end_one1).setOnClickListener {
            val slide = Slide()
            //slide.addTarget(R.id.tv_end_one1)如果不加target,就是对Window的视图进行转场了
            slide.duration = 1000
            window.returnTransition = slide
            finishAfterTransition()
        }
    }
}

效果如下:
实际效果

Transition Overlap

默认情况下,内容过渡动画的 进入/返回 转换会在 退出/重新进入 转换结束前一点点开始,产生一个小的重叠来让整体的效果更自然、更协调。这个行为其实可以通过 Window/Fragment 的setAllowEnterTransitionOverlap(boolean)和setAllowReturnTransitionOverlap(boolean)方法来设置,默认overlap是true,进入转换会退出转换开始后尽可能快地开始,如果设置为 false,进入转换只能在退出转换结束后开始。也可以在Theme设置。

9.SharedElement Transition

SharedElement Transition指的是不同的页面切换时,有两个有关联的组件需要进行前后的衔接。比如实现从列表页打开详情页面时,头像图片进行放大。同样地,可以通过Theme或代码进行设置四种不同的SharedElement Transition及监听。

		val inflateTransition =
            TransitionInflater.from(this).inflateTransition(R.transition.shared_image).apply {
                duration = 1000
            }
        window.sharedElementEnterTransition = inflateTransition
        window.sharedElementExitTransition = inflateTransition

要点:前后两个页面需要有转场的view设置相同transitionName值(可通过xml或代码进行设置)
在这里插入图片描述

activity_content_transition_start.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.ShareElementStartActivity">

    <TextView
        android:id="@+id/shareElement1_start"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="40dp"
        android:background="@color/color1"
        android:gravity="center"
        android:text="文本一"
        android:textSize="@dimen/text_size"
        android:transitionName="shareElement1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <ImageView
        android:id="@+id/shareElement2_start"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="20dp"
        android:src="@drawable/boy"
        android:transitionName="shareElement2"
        app:layout_constraintEnd_toEndOf="@id/shareElement1_start"
        app:layout_constraintStart_toStartOf="@id/shareElement1_start"
        app:layout_constraintTop_toBottomOf="@id/shareElement1_start" />

</androidx.constraintlayout.widget.ConstraintLayout>
class ShareElementStartActivity : AppCompatActivity() {
    private var shareElement1: View? = null
    private var shareElement2: View? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_share_element_start)
        //val inflateTransition =
        //    TransitionInflater.from(this).inflateTransition(R.transition.shared_image).apply {
        //        duration = 1000
        //    }
        //window.sharedElementEnterTransition = inflateTransition
        //window.sharedElementExitTransition = inflateTransition
        shareElement1 = findViewById(R.id.shareElement1_start)
        shareElement2 = findViewById(R.id.shareElement2_start)
        initListener()
    }

    private fun initListener() {
        shareElement1!!.setOnClickListener {
            val intent = Intent(this@ShareElementStartActivity, ShareElementEndActivity::class.java)
            val viewStringPair1 = Pair<View?, String>(shareElement1, shareElement1!!.transitionName)
            val viewStringPair2 = Pair<View?, String>(shareElement2, shareElement2!!.transitionName)
            val options = ActivityOptions.makeSceneTransitionAnimation(
                this@ShareElementStartActivity,
                viewStringPair1,
                viewStringPair2,
            )
            startActivity(intent, options.toBundle())
        }
    }
}

activity_share_element_end.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.ContentTransitionEndActivity">

    <TextView
        android:id="@+id/shareElement1_end"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_margin="20dp"
        android:background="@color/color1"
        android:gravity="center"
        android:text="文本一"
        android:textSize="@dimen/text_size"
        android:transitionName="shareElement1" />

    <ImageView
        android:id="@+id/shareElement2_end"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_margin="20dp"
        android:src="@drawable/girl"
        android:transitionName="shareElement2" />
</LinearLayout>

ShareElementEndActivity

class ShareElementEndActivity : AppCompatActivity() {
    private var shareElement1: View? = null
    private var shareElement2: View? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //val inflateTransition =
        //    TransitionInflater.from(this).inflateTransition(R.transition.shared_image).apply {
        //        duration = 1000
        //    }
        //window.sharedElementEnterTransition = inflateTransition
        //window.sharedElementExitTransition = inflateTransition
        setContentView(R.layout.activity_share_element_end)
    }
}

效果如下:
实际效果

10. 原理

  • 1.页面内View场景动效原理
    本质是属性动画。Transition负责捕获视图的起始属性和终止属性,创建属性动画,TransitionManager负责Transition的调度执行。
  • 2.转场动效原理
  1. 上述Transition和TransitionManager
  2. ViewGroupOverlay :覆盖在ViewGroup之上的视图层。是 ViewOverlay 的一个子类,除了支持可绘制对象的功能外,还添加了对 ViewGroups 上的覆盖层视图的管理能力。
  3. GhostView:这个视图在覆盖层中绘制另一个视图,而不改变父级视图。它的可见性设置为 INVISIBLE,所以父级视图不会将其绘制出来,但是它会使用自己的渲染节点在此处进行绘制。当 GhostView 设置为 INVISIBLE 时,它所遮挡的视图会变为 VISIBLE,当 GhostView 变为 VISIBLE 时,遮挡的视图会变为 INVISIBLE。
  4. ExitTransitionCoordinator :退出动画核心类,继承自ActivityTransitionCoordinator
  5. EnterTransitionCoordinator:进入动画核心类,继承自ActivityTransitionCoordinator
  6. Activity跳转和返回源码逻辑…

一起看下源码………
ActivityA -> ActivityB过程总结:

  1. ActivityA先执行退场动画
  2. ActivityA将共享元素的结束位置等属性传递给ActivityB
  3. ActivityB加载自己的布局,在onStart生命周期左右去找到共享元素 先定位到ActivityA的结束位置
  4. ActivityB开始执行过度动画,过渡到自己布局中的位置

ActivityB -> ActivityA过程总结:

  1. 将ActivityB的window背景设置成透明, 并执行非共享元素的退场动画
  2. 返回到ActivityA时,将会执行到performStart方法,并执行非共享元素的进场动画
  3. ActivityB接收到ActivityA传过来的共享元素状态,开始执行共享元素的退场动画
  4. ActivityA接收到ActivityB的共享元素状态,继续执行共享元素动画(但由于两个状态没有变化,所以并不会执行动画,会立马直接动画结束的回调)

11.注意事项和说明

  • 页面A->页面B,共享元素的transitionName要唯一
  • FragmentA->FragmentB->FragmentC ,共享元素的transitionName要唯一
  • 可以调postponeEnterTransition()延迟开启进入动效,然后在合适的时机调用startPostponedEnterTransition()开启进入动效
  • Material主题默认会将exit的transition设置成null,而enter的transition设置成Fade .如果reenter或者return transition没有明确设置,则将用exit和enter的transition替代。
  • Material主题默认会将exit的共享元素transition设置成null,而enter的共享元素transition设置成@android:transition/move. 如果reenter 或者 return transition没有明确设置,则将用exit 和enter的共享元素transition替代。
  • 默认情况下,material主题的应用中enter/return的content transition会在exit/reenter的content transitions结束之前开始播放(只是稍微早于),这样会看起来更加连贯。如果你想明确屏蔽这种行为,可以调用setWindowAllowEnterTransitionOverlap() 和 setWindowAllowReturnTransitionOverlap()方法。
  • Fragment的exit, enter, reenter, 和return transition需要调用fragment的相应方法来设置,或者通过theme的属性来设置。
  • Fragment的共享元素的enter和return transition也需要调用fragment的相应方法来设置,或者通过theme的属性来设置。

网站公告

今日签到

点亮在社区的每一天
去签到