前面學(xué)習(xí)的內(nèi)容:
Android自定義View(一) -- 初識(shí)
Android自定義View(二) -- Paint詳解
Android自定義View(三) -- drawText()
Android自定義View(四) -- Canvas
Android自定義View(五) -- 繪制順序
Android自定義View(六) -- 屬性動(dòng)畫(上)
今天繼續(xù)學(xué)習(xí)Android自定義View第七篇內(nèi)容 屬性動(dòng)畫(下)
因?yàn)闊o法錄制GIF,所以本篇內(nèi)容基本為原博
本文計(jì)劃根據(jù)HenCoder系列文章進(jìn)行學(xué)習(xí)鸠真,所以代碼風(fēng)格及博文素材可能會(huì)摘自其中悯仙。
簡(jiǎn)介
上期的內(nèi)容,對(duì)于大多數(shù)簡(jiǎn)單的屬性動(dòng)畫場(chǎng)景已經(jīng)夠用了吠卷。這期的內(nèi)容主要針對(duì)兩個(gè)方面:
針對(duì)特殊類型的屬性來做屬性動(dòng)畫锡垄;
針對(duì)復(fù)雜的屬性關(guān)系來做屬性動(dòng)畫。
TypeEvaluator
關(guān)于 ObjectAnimator祭隔,上期講到可以用 ofInt() 來做整數(shù)的屬性動(dòng)畫和用 ofFloat() 來做小數(shù)的屬性動(dòng)畫货岭。這兩種屬性類型是屬性動(dòng)畫最常用的兩種路操,不過在實(shí)際的開發(fā)中,可以做屬性動(dòng)畫的類型還是有其他的一些類型千贯。當(dāng)需要對(duì)其他類型來做屬性動(dòng)畫的時(shí)候屯仗,就需要用到 TypeEvaluator 了。
關(guān)于 TypeEvaluator 是什么和怎么用搔谴,先看一下下面的視頻吧:(簡(jiǎn)書不支持插入視頻)
https://www.bilibili.com/video/av14367465/?zw
ArgbEvaluator
如視頻中的例子魁袜,TypeEvaluator
最經(jīng)典的用法是使用 ArgbEvaluator
來做顏色漸變的動(dòng)畫。
ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
animator.start();
另外敦第,在 Android 5.0 (API 21) 加入了新的方法 ofArgb()
峰弹,所以如果你的 minSdk
大于或者等于 21(哈哈哈哈哈哈哈哈),你可以直接用下面這種方式:
ObjectAnimator animator = ObjectAnimator.ofArgb(view, "color", 0xffff0000, 0xff00ff00);
animator.start();
自定義 Evaluator
如果你對(duì) ArgbEvaluator 的效果不滿意芜果,或者你由于別的什么原因希望寫一個(gè)自定義的 TypeEvaluator鞠呈,你可以這樣寫:
public class PracticeEvaluator implements TypeEvaluator<Integer> {
private float[] startHsv = new float[3];
private float[] endtHsv = new float[3];
private float[] outHSV = new float[3];
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
//ARGB轉(zhuǎn)換成HSV
Color.colorToHSV(startValue,startHsv);
Color.colorToHSV(endValue,endtHsv);
outHSV[0] = startHsv[0] + (endtHsv[0] - startHsv[0]) * fraction;
outHSV[1] = startHsv[1] + (endtHsv[1] - startHsv[1]) * fraction;
outHSV[2] = startHsv[2] + (endtHsv[2] - startHsv[2]) * fraction;
int alpha = 1;
return Color.HSVToColor(alpha,outHSV);
}
}
ofObject()
借助于 TypeEvaluator
,屬性動(dòng)畫就可以通過 ofObject()
來對(duì)不限定類型的屬性做動(dòng)畫了右钾。方式很簡(jiǎn)單:
- 為目標(biāo)屬性寫一個(gè)自定義的
TypeEvaluator
- 使用
ofObject()
來創(chuàng)建Animator
粟按,并把自定義的TypeEvaluator
作為參數(shù)填入
private class PointFEvaluator implements TypeEvaluator<PointF> {
PointF newPoint = new PointF();
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
float x = startValue.x + (fraction * (endValue.x - startValue.x));
float y = startValue.y + (fraction * (endValue.y - startValue.y));
newPoint.set(x, y);
return newPoint;
}
}
ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();
另外在 API 21 中,已經(jīng)自帶了 PointFEvaluator
這個(gè)類霹粥,所以如果你的 minSdk
大于或者等于 21(哈哈哈哈哈哈哈哈)灭将,上面這個(gè)類你就不用寫了,直接用就行了后控。
ofMultiInt() ofMultiFloat()
在 API 引入的新的方法還有 ofMultiInt()
和 ofMultiFloat()
等庙曙,用法也很簡(jiǎn)單,不過實(shí)用性就低了一些浩淘。你有興趣的話可以去做一下了解捌朴,這里不在多做介紹。
以上這些就是對(duì) TypeEvaluator
的介紹张抄。它的作用是讓你可以對(duì)同樣的屬性有不同的解析方式砂蔽,對(duì)本來無法解析的屬性也可以打造出你需要的解析方式。有了 TypeEvaluator
署惯,你的屬性動(dòng)畫就有了更大的靈活性左驾,從而有了無限的可能。
TypeEvaluator
是本期的第一部分內(nèi)容:針對(duì)特殊的屬性來做屬性動(dòng)畫极谊,它可以讓你「做到本來做不到的動(dòng)畫」诡右。接下來是本期的第二部分內(nèi)容:針對(duì)復(fù)雜的屬性關(guān)系來做動(dòng)畫,它可以讓你「能做到的動(dòng)畫做起來更簡(jiǎn)單」轻猖。
PropertyValuesHolder 同一個(gè)動(dòng)畫中改變多個(gè)屬性
很多時(shí)候帆吻,你在同一個(gè)動(dòng)畫中會(huì)需要改變多個(gè)屬性,例如在改變透明度的同時(shí)改變尺寸咙边。如果使用 ViewPropertyAnimator
猜煮,你可以直接用連寫的方式來在一個(gè)動(dòng)畫中同時(shí)改變多個(gè)屬性:
view.animate()
.scaleX(1)
.scaleY(1)
.alpha(1);
而對(duì)于 ObjectAnimator
次员,是不能這么用的。不過你可以使用 PropertyValuesHolder
來同時(shí)在一個(gè)動(dòng)畫中改變多個(gè)屬性王带。
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
PropertyValuesHolder
的意思從名字可以看出來淑蔚,它是一個(gè)屬性值的批量存放地。所以你如果有多個(gè)屬性需要修改辫秧,可以把它們放在不同的 PropertyValuesHolder
中束倍,然后使用 ofPropertyValuesHolder()
統(tǒng)一放進(jìn) Animator
。這樣你就不用為每個(gè)屬性單獨(dú)創(chuàng)建一個(gè) Animator
分別執(zhí)行了盟戏。
AnimatorSet 多個(gè)動(dòng)畫配合執(zhí)行
有的時(shí)候绪妹,你不止需要在一個(gè)動(dòng)畫中改變多個(gè)屬性,還會(huì)需要多個(gè)動(dòng)畫配合工作柿究,比如邮旷,在內(nèi)容的大小從 0 放大到 100% 大小后開始移動(dòng)。這種情況使用 PropertyValuesHolder
是不行的蝇摸,因?yàn)檫@些屬性如果放在同一個(gè)動(dòng)畫中婶肩,需要共享動(dòng)畫的開始時(shí)間、結(jié)束時(shí)間貌夕、Interpolator 等等一系列的設(shè)定律歼,這樣就不能有先后次序地執(zhí)行動(dòng)畫了。
這就需要用到 AnimatorSet
了啡专。
ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
// 兩個(gè)動(dòng)畫依次執(zhí)行
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();
使用 playSequentially()
险毁,就可以讓兩個(gè)動(dòng)畫依次播放,而不用為它們?cè)O(shè)置監(jiān)聽器來手動(dòng)為他們監(jiān)管協(xié)作们童。
AnimatorSet
還可以這么用:
// 兩個(gè)動(dòng)畫同時(shí)執(zhí)行
animatorSet.playTogether(animator1, animator2);
animatorSet.start();
以及這么用:
// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式來精確配置各個(gè) Animator 之間的關(guān)系
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
有了 AnimatorSet
畔况,你就可以對(duì)多個(gè) Animator
進(jìn)行統(tǒng)一規(guī)劃和管理,讓它們按照要求的順序來工作慧库。它的使用比較簡(jiǎn)單跷跪,具體的用法我寫在講義里,你可以看一下齐板。
PropertyValuesHolders.ofKeyframe() 把同一個(gè)屬性拆分
除了合并多個(gè)屬性和調(diào)配多個(gè)動(dòng)畫吵瞻,你還可以在 PropertyValuesHolder
的基礎(chǔ)上更進(jìn)一步,通過設(shè)置 Keyframe
(關(guān)鍵幀)覆积,把同一個(gè)動(dòng)畫屬性拆分成多個(gè)階段听皿。例如,你可以讓一個(gè)進(jìn)度增加到 100% 后再「反彈」回來宽档。
// 在 0% 處開始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
// 時(shí)間經(jīng)過 50% 的時(shí)候,動(dòng)畫完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
// 時(shí)間見過 100% 的時(shí)候庵朝,動(dòng)畫完成度倒退到 80%吗冤,即反彈 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
animator.start();
第二部分又厉,「關(guān)于復(fù)雜的屬性關(guān)系來做動(dòng)畫」,就這么三種:
- 使用
PropertyValuesHolder
來對(duì)多個(gè)屬性同時(shí)做動(dòng)畫椎瘟; - 使用
AnimatorSet
來同時(shí)管理調(diào)配多個(gè)動(dòng)畫覆致; -
PropertyValuesHolder
的進(jìn)階使用:使用PropertyValuesHolder.ofKeyframe()
來把一個(gè)屬性拆分成多段,執(zhí)行更加精細(xì)的屬性動(dòng)畫肺蔚。
ValueAnimator 最基本的輪子
額外簡(jiǎn)單說一下 ValuesAnimator
煌妈。很多時(shí)候,你用不到它宣羊,只是在你使用一些第三方庫(kù)的控件璧诵,而你想要做動(dòng)畫的屬性卻沒有 setter / getter 方法的時(shí)候,會(huì)需要用到它仇冯。
除了 ViewPropertyAnimator 和 ObjectAnimator之宿,還有第三個(gè)選擇是 ValueAnimator。ValueAnimator 并不常用苛坚,因?yàn)樗墓δ芴A(chǔ)了比被。ValueAnimator 是 ObjectAnimator 的父類,實(shí)際上泼舱,ValueAnimator 就是一個(gè)不能指定目標(biāo)對(duì)象版本的 ObjectAnimator等缀。ObjectAnimator 是自動(dòng)調(diào)用目標(biāo)對(duì)象的 setter 方法來更新目標(biāo)屬性的值,以及很多的時(shí)候還會(huì)以此來改變目標(biāo)對(duì)象的 UI娇昙,而 ValueAnimator 只是通過漸變的方式來改變一個(gè)獨(dú)立的數(shù)據(jù)尺迂,這個(gè)數(shù)據(jù)不是屬于某個(gè)對(duì)象的,至于在數(shù)據(jù)更新后要做什么事涯贞,全都由你來定枪狂,你可以依然是去調(diào)用某個(gè)對(duì)象的 setter 方法(別這么為難自己),也可以做其他的事宋渔,不管要做什么州疾,都是要你自己來寫的,ValueAnimator 不會(huì)幫你做皇拣。功能最少严蓖、最不方便,但有時(shí)也是束縛最少氧急、最靈活颗胡。比如有的時(shí)候,你要給一個(gè)第三方控件做動(dòng)畫吩坝,你需要更新的那個(gè)屬性沒有 setter 方法毒姨,只能直接修改,這樣的話 ObjectAnimator 就不靈了啊钉寝。怎么辦弧呐?這個(gè)時(shí)候你就可以用 ValueAnimator闸迷,在它的 onUpdate() 里面更新這個(gè)屬性的值,并且手動(dòng)調(diào)用 invalidate()俘枫。
所以你看腥沽,ViewPropertyAnimator、ObjectAnimator鸠蚪、ValueAnimator 這三種 Animator今阳,它們其實(shí)是一種遞進(jìn)的關(guān)系:從左到右依次變得更加難用,也更加靈活茅信。但我要說明一下盾舌,它們的性能是一樣的,因?yàn)?ViewPropertyAnimator 和 ObjectAnimator 的內(nèi)部實(shí)現(xiàn)其實(shí)都是 ValueAnimator汹押,ObjectAnimator 更是本來就是 ValueAnimator 的子類矿筝,它們?nèi)齻€(gè)的性能并沒有差別。它們的差別只是使用的便捷性以及功能的靈活性棚贾。所以在實(shí)際使用時(shí)候的選擇窖维,只要遵循一個(gè)原則就行:盡量用簡(jiǎn)單的。能用 View.animate() 實(shí)現(xiàn)就不用 ObjectAnimator妙痹,能用 ObjectAnimator 就不用 ValueAnimator铸史。
本篇學(xué)習(xí)內(nèi)容結(jié)束,下一篇是自定義View的補(bǔ)充--硬件加速