前言
本文是Android動畫系列的最后一篇--轉(zhuǎn)場動畫,轉(zhuǎn)場動畫在大部分APP中都或多或少的有用到,例如兩個Activity之間的過渡動畫、Fragment切換時的過渡動畫等都屬于轉(zhuǎn)場動畫的范疇。
最初的轉(zhuǎn)場動畫
overridePendingTransition(int enterAnim,int exitAnim)
用來處理Activity之間的轉(zhuǎn)場動畫,常在 startActivity 或者 finsh操作之后調(diào)用。
只支持平移、縮放、透明度、旋轉(zhuǎn)四種動畫效果。
示例從MainActivity跳轉(zhuǎn)到ImageActivity,聲明兩個動畫 anim_in.xml 和 anim_out.xml分別對應圖中的 in 和 out部分
anim_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<translate
android:fromXDelta="100%p"
android:toXDelta="0" />
<alpha
android:fromAlpha="0"
android:toAlpha="1.0" />
</set>
anim_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<translate
android:fromXDelta="0"
android:toXDelta="-100%p" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0" />
</set>
MainActivity中startActivity之后調(diào)用overridePendingTransition方法
binding.btn.setOnClickListener {
val intent = Intent(this, ImageActivity::class.java)
startActivity(intent)
overridePendingTransition(R.anim.anim_in, R.anim.anim_out)
}
overridePendingTransition實現(xiàn)的過渡動畫相比較而言是有些限制的,動畫效果往往也不是那么的絲滑。
Material Design轉(zhuǎn)場動畫
支持Android5.0 (API 21)以上使用
支持三種進入和退出過渡動畫效果:
- 爆炸式(Explode) - 將視圖移入場景中心或從中移出
- 滑動式(Slide) - 將視圖從場景的其中一個邊緣移入或移出
- 淡入淡出式(Fade) - 通過更改視圖的不透明度,在場景中添加視圖或從中移除視圖
如何啟用
Materia轉(zhuǎn)場過渡或者共享元素過渡都需要開啟有兩種方式:
第一種是通過在應用theme中通過 android:windowActivityTransitions開啟
<style name="Theme.MyTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowActivityTransitions">true</item>
</style>
第二種是在使用過渡動畫的Activity中通過代碼開啟
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}
示例代碼
假設(shè)我們從MainActivity跳轉(zhuǎn)到ImageActivity ,使用Material的過渡動畫可以分為三步
步驟一 在MainActivity中聲明退出動畫效果
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
//使用淡入淡出效果
exitTransition = Fade()
}
步驟二在ImageActivity中聲明進入動畫效果
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
//使用淡入淡出效果
enterTransition = Fade()
}
最后呢在MainActivity中執(zhí)行跳轉(zhuǎn)
val intent = Intent(this, FadeActivity::class.java)
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
共享元素
Material Design除了支持Activity之間的轉(zhuǎn)場動畫,還支持兩個Activity之間共享元素的動畫設(shè)置。比如我們查看一個圖片,可以通過使用 changeImageTransform共享元素過渡來給圖片尺寸和縮放方面的變化添加動畫效果。除了 changeImageTransform之外呢,還有另外三種共享元素過渡方式
- changeBounds - 為目標視圖布局邊界的變化添加動畫效果。
- changeClipBounds - 為目標視圖裁剪邊界的變化添加動畫效果。
- changeTransform - 為目標視圖縮放和旋轉(zhuǎn)方面的變化添加動畫效果。
共享元素過渡效果的實現(xiàn)需要兩個Activity之間的‘共享’視圖指定同一個名稱,例如從MainActivity 中的 ImageView 到 ImageActivity 中的ImageView 可以這樣去設(shè)置:
MainActivity
binding.shareImage1.setOnClickListener {
val intent = Intent(this, ImageActivity::class.java)
//只有一個共享元素視圖
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
binding.shareImage1,
"shareImage"
)
startActivity(intent, options.toBundle())
}
ImageActivity
override fun onCreate(savedInstanceState: Bundle?) {
...
ViewCompat.setTransitionName(binding.shareImage, "shareImage")
}
MainActivity中的 ActivityOptionsCompat.makeSceneTransitionAnimation(Activity activity,View shareElement,String sharedElementName) 方法接收一個需要共享的視圖以及名稱,sharedElementName和ImageActivity中的共享視圖設(shè)置的transitionName保持一致。
如果需要設(shè)置多個共享元素動畫,可以使用Pair
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, Pair(binding.shareImage1, "shareImage"),
Pair(binding.shareText, "shareText")
)
效果圖

Android Material Motion
導入和使用
Material Components for Android 庫版本 1.4.0-alpha01及以上
該庫有兩種實現(xiàn),一種是基于AndroidX Transition Library ,另外一種是基于Android Transition Framework,兩者之前的區(qū)別如下
AndroidX
在 com.google.android.material.transition 包下
支持 API Level 14+
支持 Fragments 和 Views,不支持 Activities 和 Windows
Platform
在 com.google.android.material.transition.platform 包下
支持 API Level 21+
支持 Fragments、 Views,、Activities 和 Windows
從對比上來看,兩者兼容的最低版本不同分別是Android 3.0和Android 5.0 ,但是AndroidX不支持Activities和Windows,對于需要實現(xiàn)Activity之間過渡效果的需求不太友好,相對而講Platform支持的更全面,而且現(xiàn)在市場上Android 5.0最為最低版本可以覆蓋大部分機型,Platform 就可以作為首選實現(xiàn)。
過渡模式
Material Components for Android 庫提供了四種motion模式,可以根據(jù)需求靈活選擇實現(xiàn):
Container transform
Shared axis
Fade through
Fade
Container transform
容器轉(zhuǎn)換模式設(shè)計用于包含容器的UI元素之間的轉(zhuǎn)換。此模式在兩個UI元素之間創(chuàng)建可見連接。不同于傳統(tǒng)的共享元素動畫,它并不圍繞 單一的共享內(nèi)容而設(shè)計的,相反,這里的共享元素可以是包含 View 或者 ViewGroup 的邊界容器。

示例代碼
Fragment之間的跳轉(zhuǎn)
FragmentA展示一個兩列的Grid列表,每個Item中使用CardView布局,點擊列表Item觸發(fā)跳轉(zhuǎn)到FragmentB。
item_recycler_list.xml
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
MyAdapter - onBindViewHolder(holder:Myholder,position:Int)
holder.cardView.transitionName = "shared_image$position"
設(shè)置cardview的transitionName,這里使用position來保證transiotionName的唯一性。這個CardView作為startView。
fragment_b.xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
android:transitionName="shared_image">
fragment布局中聲明endView,這里使用LinearLayout來作為endView,使用android:transitionName屬性來進行設(shè)置。
Tips: android:transitionName是API 21才支持的,低于API 21請使用setTransitionName方法進行設(shè)置。
FragmentA.kt
adapter.clickListener = { holder, _ ->
(activity as ContainerTransformActivity).replace(holder.cardView, "shared_image")
}
ContainerTransformActivity.kt
fun replace(view: View, name: String) {
val fragmentB = FragmentB()
supportFragmentManager.beginTransaction()
.addSharedElement(view, name)
.replace(R.id.fragment_container, fragmentB, "fragmentB")
.addToBackStack("fragmentB")
.commit()
}
addSharedElement兩個參數(shù)一個是startView,在本示例中是item中的CardView,另外一個參數(shù)是endView的transitionName,設(shè)置完成后切換到FragmentB。
最后一步需要設(shè)置過渡模式
FragmentA
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialElevationScale(false).apply {
duration = 250
}
reenterTransition = MaterialElevationScale(true).apply {
duration = 250
}
}
FragmentA中的過渡效果可以在replace FragmentB之前任意位置設(shè)置。
FragmentB
//第一種
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform()
}
//第二種
val fragmentB = FragmentB()
fragmentB.sharedElementEnterTransition = MaterialContainerTransform()
FragmentB中進入過渡效果有兩種設(shè)置方式。
設(shè)置完成后就可以看到效果了。
Shared axis
共享軸模式用于具有空間或?qū)Ш疥P(guān)系的UI元素之間的轉(zhuǎn)換。此模式使用x、y或z軸上的共享轉(zhuǎn)換來加強元素之間的關(guān)系。

示例代碼
Activity之間使用
ActivityA中設(shè)置退出動畫
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
val exit = MaterialSharedAxis(MaterialSharedAxis.Z, true)
window.exitTransition = exit
默認是整個視圖參與轉(zhuǎn)換,包括狀態(tài)欄和導航欄,如果需要排除的話,可以使用如下代碼設(shè)置:
val exit = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
//1.指定轉(zhuǎn)換視圖
addTarget(R.id.root)
//2.排除狀態(tài)欄和導航啦參與轉(zhuǎn)換
excludeTarget(android.R.id.statusBarBackground, true)
excludeTarget(android.R.id.navigationBarBackground, true)
}
ActivityB中設(shè)置進入動畫
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
val enter = MaterialSharedAxis(MaterialSharedAxis.Z, true)
window.enterTransition = enter
最后在ActivityA中進行跳轉(zhuǎn)
val intent = Intent(this, ActivityB::class.java)
val scene = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
startActivity(intent, scene)
View之間使用
val sharedTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
//該方法會將root視圖進行監(jiān)控,root內(nèi)部視圖可見性變化之后觸發(fā)動畫
android.transition.TransitionManager.beginDelayedTransition(root, sharedTransition)
image1.visibility = View.GONE
image2.visibility = View.VISIBLE
image1和image2是 root的內(nèi)部子視圖,通過TransitionManager.beginDelayedTransition(root, sharedTransition)將root進行監(jiān)控后,image1和image2視圖可見度變化后會直接觸發(fā)動畫效果。
Fade Through
淡入模式用于相互之間沒有很強關(guān)系的UI元素之間的轉(zhuǎn)換。

Fade Through的使用方式和Shared axis是一樣的,可以通過
MaterialFadeThrough來實現(xiàn)。
Fade
淡出模式用于在屏幕范圍內(nèi)進入或退出的UI元素,例如在屏幕中心淡出的對話框。

示例代碼
showButton.setOnClickListener {
val materialFade = MaterialFade().apply {
duration = 150L
}
TransitionManager.beginDelayedTransition(container, materialFade)
fab.visibility = View.VISIBLE
}
淡出模式可以用于Dialog、menu等控件的顯示。
總結(jié)
轉(zhuǎn)場動畫可以賦給一個應用活力,增加流暢性和可交互性,讓用戶在使用應用中不那么生硬,這也是我們需要掌握的。在實際需求中可以根據(jù)場景來采用不同的轉(zhuǎn)場動畫實現(xiàn)策略。如果覺得文章對您有幫助,請幫忙點個贊(#.#)。