Android自定義View(七) -- 屬性動(dòng)畫(下)

前面學(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();  

image

另外敦第,在 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);
    }
}
image

ofObject()

借助于 TypeEvaluator,屬性動(dòng)畫就可以通過 ofObject() 來對(duì)不限定類型的屬性做動(dòng)畫了右钾。方式很簡(jiǎn)單:

  1. 為目標(biāo)屬性寫一個(gè)自定義的 TypeEvaluator
  2. 使用 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();  

image

另外在 API 21 中,已經(jīng)自帶了 PointFEvaluator 這個(gè)類霹粥,所以如果你的 minSdk 大于或者等于 21(哈哈哈哈哈哈哈哈)灭将,上面這個(gè)類你就不用寫了,直接用就行了后控。

image
image

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

image

而對(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();  

image

使用 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();  

image

第二部分又厉,「關(guān)于復(fù)雜的屬性關(guān)系來做動(dòng)畫」,就這么三種:

  1. 使用 PropertyValuesHolder 來對(duì)多個(gè)屬性同時(shí)做動(dòng)畫椎瘟;
  2. 使用 AnimatorSet 來同時(shí)管理調(diào)配多個(gè)動(dòng)畫覆致;
  3. 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ǔ)充--硬件加速

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怯伊,一起剝皮案震驚了整個(gè)濱河市琳轿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耿芹,老刑警劉巖崭篡,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吧秕,居然都是意外死亡琉闪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門砸彬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颠毙,“玉大人,你說我怎么就攤上這事砂碉≈郏” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵增蹭,是天一觀的道長(zhǎng)滴某。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么壮池? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任偏瓤,我火速辦了婚禮杀怠,結(jié)果婚禮上椰憋,老公的妹妹穿的比我還像新娘。我一直安慰自己赔退,他們只是感情好橙依,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硕旗,像睡著了一般窗骑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漆枚,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天创译,我揣著相機(jī)與錄音,去河邊找鬼墙基。 笑死软族,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的残制。 我是一名探鬼主播立砸,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼初茶!你這毒婦竟也來了颗祝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤恼布,失蹤者是張志新(化名)和其女友劉穎螺戳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體折汞,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡倔幼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了字支。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凤藏。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖堕伪,靈堂內(nèi)的尸體忽然破棺而出揖庄,到底是詐尸還是另有隱情,我是刑警寧澤欠雌,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布蹄梢,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏禁炒。R本人自食惡果不足惜而咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望幕袱。 院中可真熱鬧暴备,春花似錦、人聲如沸们豌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)望迎。三九已至障癌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辩尊,已是汗流浹背涛浙。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摄欲,地道東北人轿亮。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蒿涎,于是被迫代替她去往敵國(guó)和親哀托。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容