Android動畫:兩大必學動畫詳解之屬性動畫(Property Animation)

引言

????屬性動畫是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?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

????}

}

圖1-2 ofFloat應用效果圖

? ? 在某些情況下也許你并不需要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傳入的值是如何變化的。

圖1-3 ofInt源碼
圖1-4 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-5 ofArgb效果

1.2 ofObject

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

圖1-6?ofObject源碼

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

圖1-6 動畫初始化操作源碼

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

圖1-7?sIntEvaluator、sFloatEvaluator ?實例化

????如果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一個方法。

圖1-8 TypeEvaluator

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

圖1-9?IntEvaluator源碼

? ? 從上面IntEvaluator的源碼可以看出,evaluate方法對值做了處理,其中fraction是變化的百分比,然后從startValue開始按照線性速率向endValue變化。因此,如果想要自定義TypeEvaluator ,只需要在evaluate方法上下功夫就可以了。

1.4 ofPropertyValuesHolder

? ? ofPropertyValuesHolder方法構造并返回一個指定在PropertyValuesHolder內(nèi)的值進行動畫的ValueAnimator。通過下面的源碼可以看出來,ofPropertyValuesHolder需要傳入至少一個PropertyValuesHolder。

圖1-10?ofPropertyValuesHolder源碼

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

? ??PropertyValuesHolder的方法有很多,圖1-11的方法是最重要的。

圖1-11 PropertyValuesHolder方法

? ? 首先看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的值。

圖1-12?getAnimatedValue(String propertyName) 源碼
圖1-13?getAnimatedValue()源碼

? ? 此時可能會想到,之前使用的ofFloat不是也可以傳遞多值嗎?它又是怎么操作的?

????其實查看ofFloat源碼,如下圖1-14就可以發(fā)現(xiàn),ofFloat傳遞多值也是實例化了一個PropertyValuesHolder對象。因為沒有產(chǎn)生多個PropertyValuesHolder對象,所以通過getAnimatedValue()方法獲取到的只是一個PropertyValuesHolder對象的值。

1-14 ofFloat源碼

? ? 再來看看ofKeyframe方法,個人認為ofKeyframe方法是PropertyValuesHolder最重要的方法。ofKeyframe能更簡單的實現(xiàn)動畫,而不需要使用自定義TypeEvaluator。

? ? Keyframe這個類包含一個動畫的 time/value對,ValueAnimator使用Keyframe定義動畫目標在整個動畫過程中所要用的值,ValueAnimator在當前Keyframe的值和下一個Keyframe的值之間動畫。

? ? 先看使用ofKeyframe方法的效果圖,如1-15所示。

圖1-15?Keyframe使用效果圖

? ? 再看看具體代碼是怎么寫的,代碼如下所示:

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()

}

圖2-1 alpha效果圖

????上面代碼中,使用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()

圖2-2 LineView效果圖

? ? 再來看看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所示:

圖2-3 LineView使用get方法效果圖

? ? 如果不提供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所示:

圖3-1 Builder類

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í)行。

圖3-2 AnimatorSet效果圖

? ? 如果有需要,還可以繼續(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)動畫的多種效果。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容