WHY?
我們先來看一張圖
這是我們只用了 ChangeBounds 這一個(gè) Transition 的情況。我們 focus 到色塊字體處债蜜,可以看到饱搏,當(dāng)兩個(gè)場(chǎng)景進(jìn)行切換的時(shí)候,位置和 bounds 的變化都有過渡效果寥殖,看起來很自然,但是色塊背景顏色的變化以及色塊內(nèi)字體的變化涩蜘,都有種閃現(xiàn)的感覺嚼贡,視覺上效果并不是很好。其實(shí)這個(gè)效果是符合 ChangeBounds 的同诫,查看 ChangeBounds 的源碼就可以發(fā)現(xiàn)粤策,它內(nèi)部并沒有對(duì)字體顏色及背景色做過渡動(dòng)畫。所以误窖,要實(shí)現(xiàn)字體顏色和背景顏色能平滑過渡叮盘,我們就需要自定義 Transition。
如何自定義屬于自己的 Transition
通過前面的介紹霹俺,我們已經(jīng)知道了 Transition 有兩個(gè)主要任務(wù):
- 捕獲前后想要跟蹤的屬性值柔吼,
- 根據(jù)屬性值變化構(gòu)建出自己的動(dòng)畫并執(zhí)行。
所以自定義 Transition 丙唧,我們就圍繞著兩個(gè)方向去處理就可以了愈魏。其實(shí)自定義 Transition 也比較簡單,大致上分為兩步:
- 繼承 Transition 并實(shí)現(xiàn) captureStartValues 和 captureEndValues 兩個(gè)方法想际。
- 重寫 createAnimator 培漏,根據(jù) startValues 和 endValues 創(chuàng)建自己的 Animator
下面就以平滑改變背景色作為栗子
繼承 Transition 并實(shí)現(xiàn) captureStartValues 和 captureEndValues
class BackgroundColorTransition : Transition() {
private val PROPNAME_BACKGROUND ="com.wangguan.transitions.material.custom.BackgroundColorTransition:background"
override fun captureStartValues(transitionValues: TransitionValues?) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues?) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues?) {
// 將需要跟蹤的屬性塞到 values 里面
transitionValues?.also {
it.values[PROPNAME_BACKGROUND] = it.view.background
}
}
這里我們都實(shí)現(xiàn)了 ** captureStartValues** 和 ** captureEndValues** ,這兩個(gè)方法都是捕獲想要跟蹤的屬性胡本,所以里面的具體實(shí)現(xiàn)牌柄,我們調(diào)用了 captureValues 方法來捕獲屬性。transitionValues 包含一個(gè) view 和一個(gè)屬性的 map侧甫,這里我們需要將我們要跟蹤的屬性塞進(jìn)這個(gè) map 里面友鼻。這里我們關(guān)心的是 view 的背景顏色傻昙,所以將 view 的 background 塞進(jìn) key 為 PROPNAME_BACKGROUND 的 map 里面。
為了保證 key 的唯一性彩扔,官方建議 key 的聲明最好遵循以下格式:
package_name:transition_name:property_name
這里我們就將需要跟蹤的屬性告知 Transition 了妆档,接下來就是創(chuàng)建相應(yīng)動(dòng)畫了。
重寫 createAnimator
override fun createAnimator(
sceneRoot: ViewGroup?,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
startValues ?: return null
endValues ?: return null
val view: View = endValues.view
// 根據(jù)屬性 key 取出前后的背景
val startBackground = startValues.values[PROPNAME_BACKGROUND] as Drawable?
val endBackground = endValues.values[PROPNAME_BACKGROUND] as Drawable?
// 格式判斷
if (startBackground is ColorDrawable && endBackground is ColorDrawable) {
// 前后顏色發(fā)生改變才執(zhí)行動(dòng)畫
if (startBackground.color != endBackground.color) {
val animator = ValueAnimator.ofObject(
ArgbEvaluator(),
startBackground.color, endBackground.color
)
animator.addUpdateListener { animation ->
val value = animation.animatedValue
if (null != value) {
// 不斷更新背景以達(dá)到平滑過渡效果
view.setBackgroundColor(value as Int)
}
}
return animator
}
}
return null
}
當(dāng)這個(gè)方法被 framework 調(diào)用的時(shí)候虫碉,它會(huì)給我們傳場(chǎng)景的根視圖和 startValues 和 endValues 贾惦。通過 key 就可以拿到我們關(guān)心的屬性值了,拿到前后屬性值敦捧,就可以通過前后的變化創(chuàng)建我們自己的 animator须板。至此,一個(gè)簡單的 BackgroundColorTransition 就完成了兢卵∠肮澹看下最終效果:
One More Thing
那么在一次 Transition 變化中, createAnimator 會(huì)執(zhí)行多少次呢秽荤?Transition 本來就是將前后 scene 做差異動(dòng)畫的甜奄,所以,createAnimator 的調(diào)用次數(shù)其實(shí)就和前后 scene 有關(guān)窃款。
就拿本例來看
scene_first.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:id="@+id/layout_containers"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:background="@color/colorAccent"
android:text="頭像"
android:textColor="@android:color/white"
android:textSize="28sp"
app:layout_constraintBottom_toBottomOf="@id/image_icon"
app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintTop_toTopOf="@id/image_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
scene_second.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:id="@+id/layout_containers"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:minWidth="200dp"
android:minHeight="200dp"
android:src="@mipmap/ic_launcher"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@android:color/holo_orange_dark"
android:text="頭像"
android:textColor="@android:color/black"
android:textSize="28sp"
app:layout_constraintLeft_toLeftOf="@id/image_icon"
app:layout_constraintRight_toRightOf="@id/image_icon"
app:layout_constraintTop_toBottomOf="@id/image_icon" />
<TextView
android:id="@+id/text_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:padding="16dp"
android:text="這是詳情這是詳情這是詳情這是詳情
這是詳情這是詳情這是詳情這是詳情這是詳情這是詳情
這是詳情這是詳情這是詳情這是詳情這是詳情這是詳情
這是詳情這是詳情這是詳情這是詳情這是詳情這是詳情
這是詳情這是詳情這是詳情"
android:textSize="18sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
從以上可以看到课兄,場(chǎng)景二較場(chǎng)景一多了 id 為 text_detail 的 TextView,它在場(chǎng)景一中是找不到相對(duì)應(yīng) id 控件的晨继,那么 createAnimator 會(huì)回調(diào)嗎烟阐?答案是會(huì)的,它會(huì)從 null 變到 text_detail紊扬,所以蜒茄,就本例來說,createAnimator 一共回調(diào)了3 次餐屎。
androidx.appcompat.widget.AppCompatImageView{aec2280 V.ED..... ......ID 0,0-216,216 #7f0800a5 app:id/image_icon}
androidx.appcompat.widget.AppCompatImageView{78b6a29 V.ED..... ......ID 0,0-216,216 #7f0800a5 app:id/image_icon}
createAnimator androidx.appcompat.widget.AppCompatTextView{80de0b9 V.ED..... ......ID 264,52-432,164 #7f080140 app:id/text_name}
androidx.appcompat.widget.AppCompatTextView{56ae1ae V.ED..... ......ID 264,52-432,164 #7f080140 app:id/text_name}
createAnimator null
androidx.appcompat.widget.AppCompatTextView{9d94b4f V.ED..... ......ID 0,880-1080,1427 #7f080138 app:id/text_detail}
最后
BackgroundColorTransition.kt
祝好
共勉
不喜勿噴