引言
????屬性動畫是Android 3.0(API 11)加入的,如果想在之前的版本使用屬性動畫,建議使用nineoldandroids,網(wǎng)址是:http://nineoldandroids.com。
? ? 屬性動畫的加入產(chǎn)生了一個新的問題,為什么要加入屬性動畫呢?
? ? View動畫雖然簡單,但也存在著一些問題:
? ? ①只能作用于View對象。
? ??②不能改變真是屬性值,而只是改變了視覺效果。
? ??③只有四種動畫效果,效果種類少。
????而屬性動畫(Property Animation)是一個強大的動畫框架,允許作用在任意對象上??梢噪S著時間的變化改變?nèi)魏螌ο蟮膶傩裕还苓@個對象是否繪制在屏幕上,然后通過對屬性的應用而達到動畫的效果。
1 ValueAnimator
? ??ValueAnimator是屬性動畫重要的類,ObjectAnimator也是繼承自ValueAnimator,ValueAnimator有五個常用的方法。

1.1 ofFloat、ofInt、OfArgb
? ? ofFloat構造并返回一個在float值之間動畫的ValueAnimator。傳入一個參數(shù)時這個參數(shù)就是目標值,但是只有一個參數(shù)的情況很少用,因為無法確定起始值是什么。所以通常情況下,需要傳入兩個或者以上的參數(shù)。先看看使用方法,如下代碼所示。
? ??var anim: ValueAnimator = ValueAnimator.ofFloat(1f,0f,1f)
????anim.duration =3000
????anim.start()
? ? 方法很簡單,動畫開始之后可以使用addUpdateListener方法添加AnimatorUpdateListener對動畫進行監(jiān)聽。addUpdateListener方法是很重要的方法,因為ValueAnimator主要是對值進行操作的動畫,因此需要通過監(jiān)聽去獲取相應的值來進行操作以完成動畫效果。
anim.addUpdateListener{
? ? var value =it.animatedValue as Float
????Log.d("MainActivity", value.toString())
}
? ? 日志比較長我就不做截圖或者復制了。從日志上可以看出來,值得變化是從1.0f開始然過渡到0.0f再過渡到1.0f。因為沒有設置插值器,因此使用的是默認插值器AccelerateDecelerateInterpolator,這個插值器的效果是開始和結束慢中間加速。將value應用在Alpha上可以呈現(xiàn)如下圖1-2效果。
private fun myOfFloat() {
????var anim: ValueAnimator = ValueAnimator.ofFloat(1.0f,0.0f,1.0f)
????anim.duration = 3000
????tv.setOnClickListener{
????????anim.start()
????}
????anim.addUpdateListener{
????????var value =it.animatedValue as Float
????????Log.d("MainActivity", value.toString())
????????tv.alpha = value
????}
}

? ? 在某些情況下也許你并不需要float類型,而只是需要使用Int類型,就可以使用ofInt方法。先看看使用方法,代碼如下所示:
var anim:ValueAnimator = ValueAnimator.ofInt(0,1)
anim.duration =1000
anim.start()
????ofArgb構造并返回一個在顏色值之間變化的ValueAnimator。ofArgb其實和ofInt本質(zhì)上是一樣的,可以從下圖1-3、1-4源碼可看出來,ofArgb只是設置了估值器(Evaluator),?ArgbEvaluator是一個估值器,它決定了ofArgb傳入的值是如何變化的。


? ? 使用ofArgb的方式也很簡單,例如下面方法,傳入兩個32位顏色值即可達到圖1-5效果。
private fun myOfArgb() {
????var startColor = resources.getColor(R.color.colorAccent)
????var enColor = resources.getColor(R.color.colorPrimary)
????var anim: ValueAnimator = ValueAnimator.ofArgb(startColor,enColor)
????anim.duration =3000
? ? tv.setOnClickListener{
? ? ? ? anim.start()
????}
? ? anim.addUpdateListener{
? ? ? ? var color: Int =it.animatedValue as Int
????????tv.setBackgroundColor(color)
????}
}

1.2 ofObject
????ofObject構造并返回一個在Object對象之間變化的ValueAnimator。通過下面圖1-6源碼可以看出來,ofObject至少需要傳兩個參數(shù)。

????evaluator 傳入的是 null,Object只能傳入兩種類型,一種是float,另一種是int類型,傳入其他的類型會把報錯??梢栽趧赢嬮_始時初始化中看到相關的操作,源碼如下:

? ? 再往下查看源碼,可以看出來sIntEvaluator是一個IntEvaluator,sFloatEvaluator是一個FloatEvaluator。

????如果evaluator 傳入的是 null,Object傳入的是float類型,則可以把ofObject當成ofFloat使用,如下所示:
var anim: ValueAnimator = ValueAnimator.ofObject(null,0.0f,1.0f)
????如果TypeEvaluator傳入的是ArgbEvaluator,Object傳入的是32位顏色值,則可以把ofObject當成ofArgb使用,如下所示:
var startColor =resources.getColor(R.color.colorAccent)
var enColor =resources.getColor(R.color.colorPrimary)
var anim: ValueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), startColor, enColor)
? ??ofObject的用處不止于此,它強大的地方在于可以自定義TypeEvaluator,然后傳入任意對象。本文將下一小節(jié)進行講解。
1.3?TypeEvaluator
? ? 看了這么多關于TypeEvaluator的應用,肯定會很好奇TypeEvaluator到底是個什么東西。TypeEvaluator其實是一個接口,只有evaluate一個方法。

? ? 以IntEvaluator為例子,可以看出來IntEvaluator實現(xiàn)了TypeEvaluator。

? ? 從上面IntEvaluator的源碼可以看出,evaluate方法對值做了處理,其中fraction是變化的百分比,然后從startValue開始按照線性速率向endValue變化。因此,如果想要自定義TypeEvaluator ,只需要在evaluate方法上下功夫就可以了。
1.4 ofPropertyValuesHolder
? ? ofPropertyValuesHolder方法構造并返回一個指定在PropertyValuesHolder內(nèi)的值進行動畫的ValueAnimator。通過下面的源碼可以看出來,ofPropertyValuesHolder需要傳入至少一個PropertyValuesHolder。

? ? 那PropertyValuesHolder又是什么呢?PropertyValuesHolder類保存有關屬性的信息以及該屬性在動畫中應該呈現(xiàn)的值。PropertyValuesHolder對象可用于使用ValueAnimator或ObjectAnimator創(chuàng)建并行操作多個不同屬性的動畫。
? ??PropertyValuesHolder的方法有很多,圖1-11的方法是最重要的。

? ? 首先看ofFloat的使用方法,下面的代碼先實例化了兩個PropertyValuesHolder對象,然后再把這兩個對象傳入ofPropertyValuesHolder生成一個ValueAnimator。
private fun startOfPropertyValuesHolder() {
???val valuesHolderX = PropertyValuesHolder.ofFloat("getX",0.0f,10.0f)
????val valuesHolderY = PropertyValuesHolder.ofFloat("getY",0.0f,100.0f)
????val anim = ValueAnimator.ofPropertyValuesHolder(valuesHolderX,valuesHolderY)
????anim.duration =3000
? ? anim.addUpdateListener{
? ? ? ? var getX = it.getAnimatedValue("getX") as Float
????}
? ? anim.start()
}
? ? 上面的代碼中,查看源碼發(fā)現(xiàn)通過getAnimatedValue(String propertyName)方法能獲取指定的propertyName相對應的PropertyValuesHolder的值。還可以通過查看源碼知道,使用getAnimatedValue()只能獲取第一個PropertyValuesHolder的值。


? ? 此時可能會想到,之前使用的ofFloat不是也可以傳遞多值嗎?它又是怎么操作的?
????其實查看ofFloat源碼,如下圖1-14就可以發(fā)現(xiàn),ofFloat傳遞多值也是實例化了一個PropertyValuesHolder對象。因為沒有產(chǎn)生多個PropertyValuesHolder對象,所以通過getAnimatedValue()方法獲取到的只是一個PropertyValuesHolder對象的值。

? ? 再來看看ofKeyframe方法,個人認為ofKeyframe方法是PropertyValuesHolder最重要的方法。ofKeyframe能更簡單的實現(xiàn)動畫,而不需要使用自定義TypeEvaluator。
? ? Keyframe這個類包含一個動畫的 time/value對,ValueAnimator使用Keyframe定義動畫目標在整個動畫過程中所要用的值,ValueAnimator在當前Keyframe的值和下一個Keyframe的值之間動畫。
? ? 先看使用ofKeyframe方法的效果圖,如1-15所示。

? ? 再看看具體代碼是怎么寫的,代碼如下所示:
private fun startKeyFrame() {
????var progressBar2 = findViewById(R.id.progressBar3)
????var key1 = Keyframe.ofInt(0.0f, 0)
????var key2 = Keyframe.ofInt(0.5f, 100)
????var key3 = Keyframe.ofInt(1f, 80)
????var propertyValuesHolder = PropertyValuesHolder.ofKeyframe("progress", key1, key2, key3)
????var anim = ValueAnimator.ofPropertyValuesHolder(propertyValuesHolder)
????anim.duration =3000
? ? var textView = findViewById(R.id.textView)
????textView.setOnClickListener{
? ? ? ? anim.start()
????}
? ? anim.addUpdateListener{
? ? ? ? var value =it.animatedValue as Int
????????progressBar2.progress = value
????????textView.text = value.toString().plus("%")
????}
}
? ? 可以看見,首先使用Keyframe.ofInt獲取Keyframe對象,傳入兩個參數(shù),第一個參數(shù)是動畫的百分比,第二個參數(shù)是相對應的數(shù)據(jù)值,接著就是創(chuàng)建PropertyValuesHolder傳入OfPropertyValuesHolder方法中,最后可以通過addUpdateListener方法獲取動畫數(shù)值對progressBar進行操作完成動畫效果。
? ? 其它方法我就不做過多的說明了,因為我想不到有什么實例,哈哈哈哈~。但有幾個參數(shù)還是要了解:
? ??TypeConverter<T, V>:該類是一個抽象類,可將類型T轉換成類型V,當動畫中的屬性類型和值類型不相同時這是必要的,它的核心方法是convert。
? ? Property:該類是一個抽象類,用于表示宿主對象的可變值。
????Path:傳入Path表示沿著path動畫。
2 ObjectAnimator
? ? ObjectAnimator繼承自ValueAnimator,支持目標對象上的屬性進行動畫。該類的構造函數(shù)使用參數(shù)定義將要動畫的目標對象以及動畫的屬性名稱,然后在內(nèi)部選取合適的set/get方法,然后根據(jù)需要調(diào)用動畫的屬性。
????在代碼中實現(xiàn)ObjectAnimator,代碼如下所示,效果圖如圖2-1:
var anim = ObjectAnimator.ofFloat(textView, "Alpha", 1.0f, 0.0f, 1.0f)
anim.duration =1500
textView.setOnClickListener{
? ? anim.start()
}

????上面代碼中,使用ofFloat構造ObjectAnimator,第一個參數(shù)就是目標對象,第二個參數(shù)是屬性名,第三、四、五參數(shù)是具體動畫數(shù)值。動畫是如何實現(xiàn)的呢?是找到textView中的alpha屬性進行賦值使用嗎?但其實textview沒有alpha屬性、view也沒有alpha屬性。
? ?實際上alpha這個屬性名用于反射,通過屬性名獲取setAlpha方法來使用,查看源碼發(fā)現(xiàn)TextView中沒有setAlpha方法,但是View中有setAlpha方法,因此實際上是調(diào)用了View的setAlpha方法。
????同理,得到下面的例子:
var anim = ObjectAnimator.ofFloat(textView, "TranslationX", 0f,200f) //對象x軸從0移動到200
var anim = ObjectAnimator.ofFloat(textView, "ScaleX", 1.5f)//對象x軸方法1.5倍
var anim = ObjectAnimator.ofFloat(textView, "Rotation", 0f,360f)//對象旋轉360度
? ? 我們也可以在自定義View中使用這種方法,但是需要注意set方法后的第一個字符必須是大寫的(駝峰命名法)。下面是自定義view的源代碼,在onDraw中畫了一條線,并提供了setPosition方法改變線的X軸。
class LineView : View {
????private var mPosition =0f
? ? constructor(context: Context?) :this(context, null)
????constructor(context: Context?, attrs: AttributeSet?) :this(context, attrs, 0)
????constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :super(context, attrs, defStyleAttr)
????override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
????????super.onMeasure(widthMeasureSpec, heightMeasureSpec)
????}
????override fun onDraw(canvas: Canvas) {
????????var paint= Paint()
????????paint.strokeWidth =20f
? ? ? ? paint.color =resources.getColor(R.color.colorAccent,null)
????????canvas.drawLine(0f, 0f, mPosition, 0f, paint)
????}
????fun setPosition(position: Float) {
????????mPosition = position
????????invalidate()
????}
}
? ? 接著實例化ObjectAnimator對象,可以看見ofFloat使用了“Position”作為屬性名,并且從X軸從0f過渡到1000f。效果圖如圖2-2所示:
var lineView = findViewById(R.id.lineView)
var anim = ObjectAnimator.ofFloat(lineView, "Position", 0f, 1000f)
anim.duration =1000
anim.start()

? ? 再來看看get方法,在LineView中加入get方法,如下所示:
fun getPosition(): Float {
return 500f
}
????get方法是用來獲取初始值用的,當傳入的參數(shù)是一個參數(shù)的時候就需要使用到get方法獲取初始值。
var anim = ObjectAnimator.ofFloat(lineView, "Position", 1000f)
? ? 動畫首先從500開始過渡到1000,并不是從0開始過渡到1000??纯葱Ч麍D,如下圖2-3所示:

? ? 如果不提供get方法程序會報錯嗎?程序該從哪開始?答案是如果不提供get方法,程序會出現(xiàn)如下提示,但是程序仍然能正常運行,并且從初始值0開始過渡到1000,如圖2-2所示。
2018-10-26 11:37:12.429 11681-11681/com.zhj.ktdemo W/PropertyValuesHolder: Method getPosition() with type null not found on target class class com.zhj.ktdemo.LineView
3?AnimatorSet
????AnimatorSet是一個按照順序執(zhí)行Animator對象的類??梢砸黄鸩シ艅赢?,也可以按順序播放,還可以延時播放。
? ? 有兩種不同的方法可以添加動畫到AnimatorSet,可以使用playTogether或者playSequentially方法一次添加一組動畫,也可以將play方法與Builder類中的方法結合,逐個添加動畫。
? ? 使用Animation.play(Aniamtor)返回一個Builder的實例,也可以直接使用Animation.Builder(Animator),他們是一樣的。接著就可以使用Builder下的方法配合傳入的Animator使用。
????Builder類有四個重要的方法,如下圖3-1所示:

after(Animator):將當前的動畫插入到傳入的動畫之后執(zhí)行
????例如:animatorSet.play(anim).after(anim2).after(anim3),anima2在anim3之后執(zhí)行,anim在anim2之后執(zhí)行
after(long):將動畫延遲執(zhí)行
? ? 例如:animatorSet.play(anim).after(3000),anim延時3000毫秒之后執(zhí)行
before(Animator):將之前的動畫插入到傳入的動畫之前執(zhí)行
? ? 例如:animatorSet.play(anim).before(anim2).before(anim3),anim在anim2之前執(zhí)行,anim2在anim3之前執(zhí)行
with(Animator):將當前動畫和傳入的動畫同時執(zhí)行
? ??例如:animatorSet.with(anim).with(anim2).with(anim3),三個動畫同時執(zhí)行?
? ? 可以看具體的使用,代碼如下所示:
var textView = findViewById(R.id.textView)
var anim = ObjectAnimator.ofFloat(textView, "TranslationX", -500f, 0f)
anim.duration =1000
var anim2 = ObjectAnimator.ofFloat(textView, "Alpha", 0f, 1f)
anim2.duration =1000
var anim3 = ObjectAnimator.ofFloat(textView, "ScaleX", 1f, 2f)
anim3.duration =2000
var anim4 = ObjectAnimator.ofFloat(textView, "ScaleY", 1f, 2f)
anim3.duration =2000
var animatorSet = AnimatorSet()
animatorSet.play(anim).with(anim2).after(anim3).after(anim4)
textView.setOnClickListener{
? ? animatorSet.start()
}
? ? 從效果圖3-2可以看出來,after(anim4)執(zhí)行在play(anim).with(anim2).after(anim3)之前,after(anim3)執(zhí)行在play(anim).with(anim2)之前,play(anim).with(anim2)同時執(zhí)行。

? ? 如果有需要,還可以繼續(xù)使用addListener添加動畫監(jiān)聽,代碼如下所示:
animatorSet.addListener(object :Animator.AnimatorListener{
????override fun onAnimationRepeat(animation: Animator?) {
????}
????override fun onAnimationEnd(animation: Animator?) {
????}
????override fun onAnimationCancel(animation: Animator?) {?
????}
????override fun onAnimationStart(animation: Animator?) {
????}
}
4 總結
? ? ValueAnimator繼承自Animator,主要是對屬性值進行操作。配合自定義估值器TypeEvaluator,可以自定義屬性值進行動畫。
? ? ObjectAnimator繼承自ValueAnimator,可以直接操作對象,只要對象中有合適的set方法,可以利用其關鍵字符當屬性名。例如:var anim = ObjectAnimator.ofFloat(textView, "Alpha", 0f, 1f),因為View中setAlpha方法,因此可使用Alpha當屬性名。如果View中提供get方法,當只傳入一個數(shù)值時,可使用get方法獲取初始值。例如:var anim = ObjectAnimator.ofFloat(lineView, "Position", 1000f),因為lineView中有getPosition方法,從該方法獲取的值可當初始值使用。
? ? AnimatorSet繼承自Animator,可以將ValueAnimator和ObjectAnimator任意組合播放。可使用Play方法獲取Builder類,配合Builder類的四個方法實現(xiàn)動畫的多種效果。