android中的Transition动画的使用

6,270 阅读5分钟

1. 简介

Transition框架是安卓4以后引入的一个转场框架。可以在场景转换时的加入转场动画(slide,fade等等)效果,使app更加炫酷。 这里需要注意的是Transition的用途有两种。

  1. Activity和Activity之间, Fragment和Fragment之间,Activity和Fragment之间。
  2. 在一个ViewGroup内的布局变化。

2. 名词解释

还需要解释下关键的名词。 Scene: 一个布局场景 Transition: 两个场景间的动态变化,即可以理解为会产生动画效果

当一个Scene到另一个Scene时,Transition的职责为如下:

  1. 获取前后两个Scene的状态
  2. 创建Animator,并计算动画效果,然后播放动画。

3. 相关动画类型

4. 动画的创建方法

4.1 xml文件

在res文件夹下创建一个transition文件夹。并在里面创建xml动画文件。

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:duration="200"
    android:transitionOrdering="sequential" />

此时如果要在代码中引用该文件时可以用下面的方法。

window.exitTransition = TransitionInflater.from(this).inflateTransition(R.transition.fade_transition)

4.2 利用代码

window.exitTransition = 
    Slide().apply {
        duration = 200
        slideEdge = Gravity.END
    }

4.3 创建多转场

利用xml:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="together">
    <explode
        android:duration="1000"
        android:interpolator="@android:interpolator/accelerate_decelerate" />
    <fade
        android:duration="1000"
        android:fadingMode="fade_in_out"
        android:interpolator="@android:interpolator/accelerate_decelerate" />
    <slide
        android:duration="500"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:slideEdge="top" />
</transitionSet>

利用代码:

val transitionSet = TransitionSet()
                transitionSet.addTransition(Fade())
                transitionSet.addTransition(Slide())
                transitionSet.setOrdering(ORDERING_TOGETHER)

5. Activity之间的转场动画

5.1 各Activity的转场

A.exitTransition(): 从A到B时A的退出动画 B.enterTransition(): 从A到B时B的进场动画 B.returnTransition(): 从B返回A时的退出动画 A.reenterTransition(): 从B返回A时的进场动画

5.2 进场退场的过渡动画

  1. explore : 将View移入场景中心或从中移出。
  2. slide : 将视图从场景的其中一个边缘移入或移出。可以利用slideEdge=Gravity.Start方式设置移入移出边缘。
  3. fade : 通过更改视图的不透明度,在场景中添加视图或从中移除视图。

系统支持将任何扩展 Visibility 类的过渡作为进入或退出过渡。

5.3 转场设置

需要在activity的onCreate中设置转场动画。

window.let {
            // 设置分享元素的转场(后面会讲到)
            it.sharedElementEnterTransition
            it.sharedElementExitTransition
            it.sharedElementReenterTransition
            it.sharedElementReturnTransition
            
            // 设置是否需动画覆盖,转场动画中动画之间会有覆盖的情况
            // 可以设置false来让动画有序的进入退出
            it.allowEnterTransitionOverlap = false
            it.allowReturnTransitionOverlap = false
            
            // 设置activity之间的转场动画
//            it.exitTransition = TransitionInflater.from(this).inflateTransition(R.transition.fade_transition)
            it.exitTransition = Slide().apply {
                duration = 200
                slideEdge = Gravity.END
            }
            it.enterTransition = Slide().apply {
                duration = 200
                slideEdge = Gravity.START

            }
            it.reenterTransition = Slide().apply {
                duration = 200
                slideEdge = Gravity.END
            }
            it.returnTransition = Slide().apply {
                duration = 200
                slideEdge = Gravity.START
            }
        }

动画设置完了,下一步我们是需要进行跳转的工作。 activity之间跳转需要用到intent, 如果需要它们之间传递数据是要用到Bundle。 没有错,我们要设置的转场动画也要用到Bundle。我们看一下下面的代码。

btn2.setOnClickListener {
    val intent = Intent()
    intent.setClass(this, ThirdActivity::class.java)
    // 利用ActivityOptions生成TransitionActivityOptions
    val transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(
        this@MainActivity
    )
    // 利用上面生成的TransitionActivityOptions生成Bundle
    startActivity(intent, transitionActivityOptions.toBundle())
}

6. Activity之间的元素共享转场

元素共享转场是Meterial设计中个人比较喜欢的设计之一。 activity之间的转场总有顿挫感,但是利用元素共享转场可以让前一个画面中的元素可以在下一个画面上流畅的显示。

6.1 转场元素设置

1. 首先我们需要让元素共享转场有效。

其实上面讲到过设置方法,如下。

window.let {
    // 设置分享元素的转场
    it.sharedElementEnterTransition
    it.sharedElementExitTransition
    it.sharedElementReenterTransition
    it.sharedElementReturnTransition
}
2. 设置transitionName

要想让元素共享,需要让前一个画面的元素的id和后一个元素的id相同。 还有需要设置transitionName,设置的地方是layout的xml上。 当然,transitionName也需要前后一致。 如下。

<ImageView
        android:id="@+id/img"
        android:layout_width="240dp"
        android:layout_height="160dp"
        android:scaleType="fitCenter"
        android:src="@mipmap/android_logo"
        android:transitionName="img" // 就是这个
        app:layout_constraintBottom_toTopOf="@id/txt"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

6.2 动画设置

我们需要为转场元素设置动画。在activity的onCreatewindow上进行设置。

 override fun onCreate(savedInstanceState: Bundle?) {
        // 为转场元素设置动画
        window.sharedElementEnterTransition = ChangeBounds()
        window.sharedElementExitTransition = ChangeBounds()
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 }

需要注意的是前后两个画面都需要为共享元素设置转场动画。当然动画可以是不一样的,这个部分根据自己需要进行设置就好了。

6.3 进行转场

val intent = Intent()
val pair1 = Pair(imgView as View, imgView.transitionName)
val pair2 = Pair(textView as View, textView.transitionName)
intent.setClass(this, SecondActivity::class.java)
val transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(
    this@MainActivity,
    pair1, pair2
)
startActivity(intent, transitionActivityOptions.toBundle())

跟上面一样也是转换成Bundle进行转场的设置。 但是因为需要共享元素,ActivityOptions.makeSceneTransitionAnimation中加入共享元素信息。 这里需要用到的是Pair的数据结构,Pair的左边是共享的view,右边是刚刚设置的transitionName。 如果共享元素只有一个的情况可以直接使用如下方法。

val intent = Intent()
intent.setClass(this, SecondActivity::class.java)
val transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(
    this@MainActivity,
    imgView as View, imgView.transitioName
)
startActivity(intent, transitionActivityOptions.toBundle())

7. 一个画面的Scene转换

除了Activity之间的转换还有一个布局内的scene转换。

7.1 创建Scene

为两个布局创建Scene。

val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene1, this)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene2, this)

除了上面的方法,也可以用下面方法进行创建。

scene1 = Scene.getSceneForLayout(sceneRoot,R.layout.scene_layout1,this)
scene2 = Scene.getSceneForLayout(sceneRoot,R.layout.scene_layout2,this)

注意的是需要创建前后两个Scene的控件的ID相同。

7.2 切换Scene

创建Transition动画。

val transition =  TransitionInflater.from(this).inflateTransition(R.transition.slide_transition)

利用TransitionManager进行Scene的切换。

TransitionManager.go(scene2, transition)

7.3 完整的代码

// 创建Scene
val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene1, this)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene2, this)
var count = 1

btn.setOnClickListener {
    count = if (count == 1) {
    // 创建transition动画
    val transition =
TransitionInflater.from(this).inflateTransition(R.transition.slide_transition)
    // Scene切换
    TransitionManager.go(scene2, transition)
        2
    } else {
    val transition =
 TransitionInflater.from(this).inflateTransition(R.transition.slide_transition)
    TransitionManager.go(scene1, transition)
        1
    }
}

8. 限制(来源:官方文档)

  • Android 版本在 4.0(API Level 14)4.4.2(API Level 19) 使用 Android Support Library’s
  • 应用于 SurfaceView 的动画可能无法正确显示。 SurfaceView 实例是从非界面线程更新的,因此这些更新与其他视图的动画可能不同步。
  • 当应用于 TextureView 时,某些特定过渡类型可能无法产生所需的动画效果。
  • 扩展 AdapterView 的类(例如 ListView)会以与过渡框架不兼容的方式管理它们的子视图。如果您尝试为基于 AdapterView 的视图添加动画效果,则设备显示屏可能会挂起。
  • 如果您尝试使用动画调整 TextView 的大小,则文本会在该对象完全调整过大小之前弹出到新位置。为了避免出现此问题,请勿为调整包含文本的视图的大小添加动画效果。

github: github.com/HyejeanMOON…