前言
本文是Android動(dòng)畫(huà)系列的最后一篇--轉(zhuǎn)場(chǎng)動(dòng)畫(huà)黔牵,轉(zhuǎn)場(chǎng)動(dòng)畫(huà)在大部分APP中都或多或少的有用到黎休,例如兩個(gè)Activity之間的過(guò)渡動(dòng)畫(huà)、Fragment切換時(shí)的過(guò)渡動(dòng)畫(huà)等都屬于轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的范疇烦绳。
最初的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
overridePendingTransition(int enterAnim灌旧,int exitAnim)
用來(lái)處理Activity之間的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),常在 startActivity
或者 finsh
操作之后調(diào)用悴晰。
只支持平移、縮放逐工、透明度铡溪、旋轉(zhuǎn)四種動(dòng)畫(huà)效果。
示例從MainActivity
跳轉(zhuǎn)到ImageActivity
泪喊,聲明兩個(gè)動(dòng)畫(huà) anim_in.xml
和 anim_out.xml
分別對(duì)應(yīng)圖中的 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
實(shí)現(xiàn)的過(guò)渡動(dòng)畫(huà)相比較而言是有些限制的棕硫,動(dòng)畫(huà)效果往往也不是那么的絲滑。
Material Design轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
支持Android5.0 (API 21)
以上使用
支持三種進(jìn)入和退出過(guò)渡動(dòng)畫(huà)效果:
- 爆炸式(Explode) - 將視圖移入場(chǎng)景中心或從中移出
- 滑動(dòng)式(Slide) - 將視圖從場(chǎng)景的其中一個(gè)邊緣移入或移出
- 淡入淡出式(Fade) - 通過(guò)更改視圖的不透明度袒啼,在場(chǎng)景中添加視圖或從中移除視圖
如何啟用
Materia轉(zhuǎn)場(chǎng)過(guò)渡或者共享元素過(guò)渡都需要開(kāi)啟有兩種方式:
第一種是通過(guò)在應(yīng)用theme
中通過(guò) android:windowActivityTransitions
開(kāi)啟
<style name="Theme.MyTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowActivityTransitions">true</item>
</style>
第二種是在使用過(guò)渡動(dòng)畫(huà)的Activity中通過(guò)代碼開(kāi)啟
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}
示例代碼
假設(shè)我們從MainActivity
跳轉(zhuǎn)到ImageActivity
哈扮,使用Material的過(guò)渡動(dòng)畫(huà)可以分為三步
步驟一 在MainActivity
中聲明退出動(dòng)畫(huà)效果
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
//使用淡入淡出效果
exitTransition = Fade()
}
步驟二在ImageActivity
中聲明進(jìn)入動(dòng)畫(huà)效果
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)場(chǎng)動(dòng)畫(huà),還支持兩個(gè)Activity之間共享元素的動(dòng)畫(huà)設(shè)置蚓再。比如我們查看一個(gè)圖片滑肉,可以通過(guò)使用 changeImageTransform
共享元素過(guò)渡來(lái)給圖片尺寸和縮放方面的變化添加動(dòng)畫(huà)效果。除了 changeImageTransform
之外呢摘仅,還有另外三種共享元素過(guò)渡方式
- changeBounds - 為目標(biāo)視圖布局邊界的變化添加動(dòng)畫(huà)效果靶庙。
- changeClipBounds - 為目標(biāo)視圖裁剪邊界的變化添加動(dòng)畫(huà)效果。
- changeTransform - 為目標(biāo)視圖縮放和旋轉(zhuǎn)方面的變化添加動(dòng)畫(huà)效果娃属。
共享元素過(guò)渡效果的實(shí)現(xiàn)需要兩個(gè)Activity之間的‘共享’視圖指定同一個(gè)名稱六荒,例如從MainActivity 中的 ImageView 到 ImageActivity 中的ImageView 可以這樣去設(shè)置:
MainActivity
binding.shareImage1.setOnClickListener {
val intent = Intent(this, ImageActivity::class.java)
//只有一個(gè)共享元素視圖
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)
方法接收一個(gè)需要共享的視圖以及名稱护姆,sharedElementName
和ImageActivity
中的共享視圖設(shè)置的transitionName保持一致。
如果需要設(shè)置多個(gè)共享元素動(dòng)畫(huà)掏击,可以使用Pair
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, Pair(binding.shareImage1, "shareImage"),
Pair(binding.shareText, "shareText")
)
效果圖
Android Material Motion
導(dǎo)入和使用
Material Components for Android 庫(kù)版本 1.4.0-alpha01及以上
該庫(kù)有兩種實(shí)現(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
從對(duì)比上來(lái)看柒凉,兩者兼容的最低版本不同分別是Android 3.0和Android 5.0 ,但是AndroidX不支持Activities和Windows篓跛,對(duì)于需要實(shí)現(xiàn)Activity之間過(guò)渡效果的需求不太友好膝捞,相對(duì)而講Platform支持的更全面,而且現(xiàn)在市場(chǎng)上Android 5.0最為最低版本可以覆蓋大部分機(jī)型愧沟,Platform 就可以作為首選實(shí)現(xiàn)蔬咬。
過(guò)渡模式
Material Components for Android 庫(kù)提供了四種motion
模式,可以根據(jù)需求靈活選擇實(shí)現(xiàn):
Container transform
Shared axis
Fade through
Fade
Container transform
容器轉(zhuǎn)換模式設(shè)計(jì)用于包含容器的UI元素之間的轉(zhuǎn)換沐寺。此模式在兩個(gè)UI元素之間創(chuàng)建可見(jiàn)連接林艘。不同于傳統(tǒng)的共享元素動(dòng)畫(huà),它并不圍繞 單一的共享內(nèi)容而設(shè)計(jì)的混坞,相反狐援,這里的共享元素可以是包含 View 或者 ViewGroup 的邊界容器。
示例代碼
Fragment之間的跳轉(zhuǎn)
FragmentA展示一個(gè)兩列的Grid列表究孕,每個(gè)Item中使用CardView布局啥酱,點(diǎn)擊列表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
來(lái)保證transiotionName
的唯一性镶殷。這個(gè)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
來(lái)作為endView绘趋,使用android:transitionName
屬性來(lái)進(jìn)行設(shè)置。
Tips: android:transitionName
是API 21才支持的颗管,低于API 21請(qǐng)使用setTransitionName
方法進(jìn)行設(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
兩個(gè)參數(shù)一個(gè)是startView,在本示例中是item中的CardView垦江,另外一個(gè)參數(shù)是endView的transitionName
拷呆,設(shè)置完成后切換到FragmentB。
最后一步需要設(shè)置過(guò)渡模式
FragmentA
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialElevationScale(false).apply {
duration = 250
}
reenterTransition = MaterialElevationScale(true).apply {
duration = 250
}
}
FragmentA中的過(guò)渡效果可以在replace FragmentB之前任意位置設(shè)置。
FragmentB
//第一種
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform()
}
//第二種
val fragmentB = FragmentB()
fragmentB.sharedElementEnterTransition = MaterialContainerTransform()
FragmentB中進(jìn)入過(guò)渡效果有兩種設(shè)置方式茬斧。
設(shè)置完成后就可以看到效果了腰懂。
Shared axis
共享軸模式用于具有空間或?qū)Ш疥P(guān)系的UI元素之間的轉(zhuǎn)換。此模式使用x项秉、y或z軸上的共享轉(zhuǎn)換來(lái)加強(qiáng)元素之間的關(guān)系绣溜。
示例代碼
Activity之間使用
ActivityA中設(shè)置退出動(dòng)畫(huà)
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
val exit = MaterialSharedAxis(MaterialSharedAxis.Z, true)
window.exitTransition = exit
默認(rèn)是整個(gè)視圖參與轉(zhuǎn)換,包括狀態(tài)欄和導(dǎo)航欄娄蔼,如果需要排除的話怖喻,可以使用如下代碼設(shè)置:
val exit = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
//1.指定轉(zhuǎn)換視圖
addTarget(R.id.root)
//2.排除狀態(tài)欄和導(dǎo)航啦參與轉(zhuǎn)換
excludeTarget(android.R.id.statusBarBackground, true)
excludeTarget(android.R.id.navigationBarBackground, true)
}
ActivityB中設(shè)置進(jìn)入動(dòng)畫(huà)
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
val enter = MaterialSharedAxis(MaterialSharedAxis.Z, true)
window.enterTransition = enter
最后在ActivityA中進(jìn)行跳轉(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)
//該方法會(huì)將root視圖進(jìn)行監(jiān)控,root內(nèi)部視圖可見(jiàn)性變化之后觸發(fā)動(dòng)畫(huà)
android.transition.TransitionManager.beginDelayedTransition(root, sharedTransition)
image1.visibility = View.GONE
image2.visibility = View.VISIBLE
image1
和image2
是 root
的內(nèi)部子視圖岁诉,通過(guò)TransitionManager.beginDelayedTransition(root, sharedTransition)
將root
進(jìn)行監(jiān)控后锚沸,image1
和image2
視圖可見(jiàn)度變化后會(huì)直接觸發(fā)動(dòng)畫(huà)效果。
Fade Through
淡入模式用于相互之間沒(méi)有很強(qiáng)關(guān)系的UI元素之間的轉(zhuǎn)換涕癣。
Fade Through的使用方式和Shared axis是一樣的哗蜈,可以通過(guò)
MaterialFadeThrough
來(lái)實(shí)現(xiàn)。
Fade
淡出模式用于在屏幕范圍內(nèi)進(jìn)入或退出的UI元素坠韩,例如在屏幕中心淡出的對(duì)話框距潘。
示例代碼
showButton.setOnClickListener {
val materialFade = MaterialFade().apply {
duration = 150L
}
TransitionManager.beginDelayedTransition(container, materialFade)
fab.visibility = View.VISIBLE
}
淡出模式可以用于Dialog、menu等控件的顯示只搁。
總結(jié)
轉(zhuǎn)場(chǎng)動(dòng)畫(huà)可以賦給一個(gè)應(yīng)用活力音比,增加流暢性和可交互性,讓用戶在使用應(yīng)用中不那么生硬氢惋,這也是我們需要掌握的洞翩。在實(shí)際需求中可以根據(jù)場(chǎng)景來(lái)采用不同的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)實(shí)現(xiàn)策略。如果覺(jué)得文章對(duì)您有幫助焰望,請(qǐng)幫忙點(diǎn)個(gè)贊(#.#)骚亿。