屬性動(dòng)畫(huà)的基本使用

  • 屬性動(dòng)畫(huà)簡(jiǎn)介

Android開(kāi)發(fā)過(guò)程中买猖,適當(dāng)?shù)氖褂靡恍﹦?dòng)畫(huà)可以讓自己的應(yīng)用看起來(lái)更棒更炫每界。最初的時(shí)候Google為了實(shí)現(xiàn)動(dòng)畫(huà)瞬测,主要提供了兩種基本動(dòng)畫(huà):
幀動(dòng)畫(huà):將一個(gè)完整的動(dòng)畫(huà)拆分成一張張單獨(dú)的圖片拓劝,然后再將它們連貫起來(lái)進(jìn)行播放
補(bǔ)間動(dòng)畫(huà):無(wú)需逐一定義每一幀底循,只要定義開(kāi)始仿滔、結(jié)束的幀惠毁,和指定動(dòng)畫(huà)持續(xù)時(shí)間。
到了Android3.0堤撵,Google推出了一種新的動(dòng)畫(huà)模式仁讨,屬性動(dòng)畫(huà)。屬性動(dòng)畫(huà)是對(duì)補(bǔ)間動(dòng)畫(huà)的進(jìn)一步完善和強(qiáng)化实昨。

  • 為什么引入屬性動(dòng)畫(huà):

關(guān)于這個(gè)問(wèn)題洞豁,可以借鑒一下郭霖的一段話:
補(bǔ)間動(dòng)畫(huà)機(jī)制其實(shí)還算是比較健全的,可以讓我們實(shí)現(xiàn)View的移動(dòng)荒给、縮放丈挟、旋轉(zhuǎn)和淡入淡出效果,并且我們還可以借助AnimationSet來(lái)將這些動(dòng)畫(huà)效果組合起來(lái)使用志电,除此之外還可以通過(guò)配置Interpolator來(lái)控制動(dòng)畫(huà)的播放速度等等等等曙咽。那么這里大家可能要產(chǎn)生疑問(wèn)了,既然之前的動(dòng)畫(huà)機(jī)制已經(jīng)這么健全了挑辆,為什么還要引入屬性動(dòng)畫(huà)呢例朱?
其實(shí)上面所謂的健全都是相對(duì)的孝情,如果你的需求中只需要對(duì)View進(jìn)行移動(dòng)、縮放洒嗤、旋轉(zhuǎn)和淡入淡出操作箫荡,那么補(bǔ)間動(dòng)畫(huà)確實(shí)已經(jīng)足夠健全了。但是很顯然渔隶,這些功能是不足以覆蓋所有的場(chǎng)景的羔挡,一旦我們的需求超出了移動(dòng)、縮放间唉、旋轉(zhuǎn)和淡入淡出這四種對(duì)View的操作绞灼,那么補(bǔ)間動(dòng)畫(huà)就不能再幫我們忙了,也就是說(shuō)它在功能和可擴(kuò)展方面都有相當(dāng)大的局限性呈野,那么下面我們就來(lái)看看補(bǔ)間動(dòng)畫(huà)所不能勝任的場(chǎng)景低矮。
注意上面我在介紹補(bǔ)間動(dòng)畫(huà)的時(shí)候都有使用“對(duì)View進(jìn)行操作”這樣的描述,沒(méi)錯(cuò)际跪,補(bǔ)間動(dòng)畫(huà)是只能夠作用在View上的商佛。也就是說(shuō),我們可以對(duì)一個(gè)Button姆打、TextView良姆、甚至是LinearLayout、或者其它任何繼承自View的組件進(jìn)行動(dòng)畫(huà)操作幔戏,但是如果我們想要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫(huà)操作玛追,抱歉,補(bǔ)間動(dòng)畫(huà)就幫不上忙了闲延∪剩可能有的朋友會(huì)感到不能理解,我怎么會(huì)需要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫(huà)操作呢垒玲?這里我舉一個(gè)簡(jiǎn)單的例子陆馁,比如說(shuō)我們有一個(gè)自定義的View,在這個(gè)View當(dāng)中有一個(gè)Point對(duì)象用于管理坐標(biāo)合愈,然后在onDraw()方法當(dāng)中就是根據(jù)這個(gè)Point對(duì)象的坐標(biāo)值來(lái)進(jìn)行繪制的叮贩。也就是說(shuō),如果我們可以對(duì)Point對(duì)象進(jìn)行動(dòng)畫(huà)操作佛析,那么整個(gè)自定義View的動(dòng)畫(huà)效果就有了益老。顯然,補(bǔ)間動(dòng)畫(huà)是不具備這個(gè)功能的寸莫,這是它的第一個(gè)缺陷捺萌。
然后補(bǔ)間動(dòng)畫(huà)還有一個(gè)缺陷,就是它只能夠?qū)崿F(xiàn)移動(dòng)膘茎、縮放桃纯、旋轉(zhuǎn)和淡入淡出這四種動(dòng)畫(huà)操作酷誓,那如果我們希望可以對(duì)View的背景色進(jìn)行動(dòng)態(tài)地改變呢?很遺憾慈参,我們只能靠自己去實(shí)現(xiàn)了呛牲。說(shuō)白了,之前的補(bǔ)間動(dòng)畫(huà)機(jī)制就是使用硬編碼的方式來(lái)完成的驮配,功能限定死就是這些,基本上沒(méi)有任何擴(kuò)展性可言着茸。
最后壮锻,補(bǔ)間動(dòng)畫(huà)還有一個(gè)致命的缺陷,就是它只是改變了View的顯示效果而已涮阔,而不會(huì)真正去改變View的屬性猜绣。什么意思呢?比如說(shuō)敬特,現(xiàn)在屏幕的左上角有一個(gè)按鈕掰邢,然后我們通過(guò)補(bǔ)間動(dòng)畫(huà)將它移動(dòng)到了屏幕的右下角,現(xiàn)在你可以去嘗試點(diǎn)擊一下這個(gè)按鈕伟阔,點(diǎn)擊事件是絕對(duì)不會(huì)觸發(fā)的辣之,因?yàn)閷?shí)際上這個(gè)按鈕還是停留在屏幕的左上角,只不過(guò)補(bǔ)間動(dòng)畫(huà)將這個(gè)按鈕繪制到了屏幕的右下角而已皱炉。正是因?yàn)檫@些原因怀估,功能更為完善和強(qiáng)大的屬性動(dòng)畫(huà)就應(yīng)運(yùn)而生了。

  • 繼承關(guān)系
繼承關(guān)系

從圖中可以看出合搅,補(bǔ)間動(dòng)畫(huà)主要是使用android.view.animation.Animation包下面的五個(gè)類來(lái)實(shí)現(xiàn)不同的動(dòng)畫(huà)效果多搀;而屬性動(dòng)畫(huà)主要是使用android.animation.Animator的ValueAnimator以及其子類ObjectAnimator來(lái)實(shí)現(xiàn)不同的動(dòng)畫(huà)效果。

  • ValueAnimator

從屬性動(dòng)畫(huà)相關(guān)對(duì)象的繼承關(guān)系可以看出灾部,ValueAnimator是比較核心的一個(gè)類康铭。官方文檔的介紹是:this class provides a simple timing engine for running animations which calculate animated values and set them on target objects...也就是說(shuō)ValueAnimator對(duì)象內(nèi)部維持了一個(gè)簡(jiǎn)單的時(shí)間引擎,計(jì)算運(yùn)行動(dòng)畫(huà)的具體值同時(shí)把它們?cè)O(shè)置給目標(biāo)對(duì)象赌髓。通俗的講就是从藤,ValueAnimator的內(nèi)部使用一種時(shí)間循環(huán)的機(jī)制來(lái)計(jì)算值與值之間的動(dòng)畫(huà)過(guò)渡,我們只需要將初始值和結(jié)束值或者中間值提供給ValueAnimator春弥,并且設(shè)置動(dòng)畫(huà)運(yùn)行時(shí)長(zhǎng)呛哟,那么ValueAnimator就會(huì)自動(dòng)完成從初始值平滑過(guò)渡到結(jié)束值這樣的效果。而且匿沛,ValueAnimator還負(fù)責(zé)管理動(dòng)畫(huà)的播放次數(shù)扫责、播放模式、以及對(duì)動(dòng)畫(huà)設(shè)置監(jiān)聽(tīng)器等逃呼。說(shuō)的廢話比較多鳖孤,先舉個(gè)栗子者娱,切身體會(huì)一下!

1.效果圖

ValueAnimator

2.具體代碼

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.setDuration(3000);
//設(shè)置監(jiān)聽(tīng)苏揣,把當(dāng)前AnimatedValue的值時(shí)時(shí)顯示到EditText上面
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float currentValue = (float) animation.getAnimatedValue();
        StringBuilder sb = new StringBuilder();
        et_Animator.setText(sb.append(currentValue).toString());
    }
});
valueAnimator.start();

ValueAnimator比較常用的方法有:設(shè)置AnimatorValue區(qū)間范圍的三個(gè)主要方法
ofFloat()黄鳍,ofInt(),ofObject()
還有設(shè)置延時(shí)播放時(shí)間的setStartDelay()方法平匈,設(shè)置重復(fù)次數(shù)的setRepeatCount()方法框沟,設(shè)置重復(fù)模式的setRepeatMode()
暫停動(dòng)畫(huà)的pause()方法,恢復(fù)動(dòng)畫(huà)的resume()等等增炭,還有很多方法忍燥,具體大家可以查閱文檔,把這個(gè)核心類的方法掌握了隙姿,使用ObjectAnimator就能更加得心應(yīng)手梅垄,因?yàn)閺睦^承關(guān)系中我們可以知道ObjectAnimator是ValueAnimator的子類。

  • ObjectAnimator

ObjectAnimator對(duì)象應(yīng)該是我們產(chǎn)生屬性動(dòng)畫(huà)效果最常用的類了输玷,ValueAnimator僅僅是對(duì)數(shù)值進(jìn)行平滑的過(guò)渡變化队丝,而如果我們想對(duì)一個(gè)對(duì)象的任意屬性進(jìn)行動(dòng)畫(huà)效果,就需要使用ObjectAnimator對(duì)象了欲鹏,還是先舉個(gè)簡(jiǎn)單栗子吧机久。

1.效果圖

Alpha動(dòng)畫(huà)

2.具體代碼

//ofFloat(需要實(shí)現(xiàn)動(dòng)畫(huà)的控件ID,需要實(shí)現(xiàn)的動(dòng)畫(huà)效果名稱,變化的區(qū)間范圍)
ObjectAnimator objAnim = bjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
objAnim.setDuration(6000);
objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float currentValue = (float) animation.getAnimatedValue();
        StringBuilder sb = new StringBuilder();
        et_Animator.setText(sb.append(currentValue).toString());
    }
});
objAnim.start();

以上代碼實(shí)現(xiàn)的效果是:在6000毫秒內(nèi)貌虾,讓EditText控件平滑的實(shí)現(xiàn)了從完全透明>完全不透明>完全透明>完全不透明的過(guò)渡效果吞加;并且對(duì)整個(gè)動(dòng)畫(huà)執(zhí)行過(guò)程設(shè)置了監(jiān)聽(tīng),時(shí)時(shí)把當(dāng)前動(dòng)畫(huà)的AnimatorValue值顯示到EditText控件上面尽狠。

根據(jù)設(shè)置參數(shù)的不同衔憨,使用ObjectAnimator對(duì)象還可以實(shí)現(xiàn)translationX(豎直平移),translationY(水平平移)袄膏,rotation(旋轉(zhuǎn))践图,scaleX(水平縮放),scaleY(豎直縮放)等其他效果沉馆。當(dāng)然還可以使用AnimatorSet對(duì)象來(lái)產(chǎn)生組合動(dòng)畫(huà)效果码党。

  • 效果圖
綜合栗子
  • 具體代碼
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_OperationAnimator;
    private EditText et_Animator;
    private RadioGroup radioGroup;
    private ObjectAnimator objAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_Animator = (EditText) findViewById(R.id.et_Animator);
        radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
        btn_OperationAnimator = (Button) findViewById(R.id.btn_OperationAnimator);

        btn_OperationAnimator.setOnClickListener(this);

    }
    @Override
    public void onClick(View v) {

        int checkedId = radioGroup.getCheckedRadioButtonId();

        switch (checkedId) {

            case R.id.rb_value:
                ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
                valueAnimator.setDuration(3000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                valueAnimator.start();
                break;

            case R.id.rb_alpha:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_translationX:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"translationX",0f, 100f, 0f, 200f, 0f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_translationY:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"translationY",0f, 100f, 0f, 200f, 0f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_rotation:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"rotation",0f, 180f, 0f, 360f, 0f);
                objAnim.setDuration(6000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_scaleX:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"scaleX", 4f, 1f, 2f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_scaleY:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"scaleY",1f, 5f, 2f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_animatorSet:
                ObjectAnimator objAnim_alpha = ObjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
                ObjectAnimator objAnim_translationX = ObjectAnimator.ofFloat(et_Animator,"translationX",0f, 100f, 0f, 200f, 0f);
                ObjectAnimator objAnim_translationY = ObjectAnimator.ofFloat(et_Animator,"translationY",0f, 100f, 0f, 200f, 0f);
                ObjectAnimator objAnim_rotation = ObjectAnimator.ofFloat(et_Animator,"rotation",0f, 180f, 0f, 360f, 0f);
                ObjectAnimator objAnim_scaleX = ObjectAnimator.ofFloat(et_Animator,"scaleX", 4f, 1f, 2f, 1f);
                ObjectAnimator objAnim_scaleY = ObjectAnimator.ofFloat(et_Animator,"scaleY",1f, 5f, 2f, 1f);
                AnimatorSet animSet = new AnimatorSet();
                animSet.play(objAnim_alpha)
                        .before(objAnim_rotation)    //在play動(dòng)畫(huà)之后執(zhí)行
                        .after(objAnim_scaleX)       //在play動(dòng)畫(huà)之前執(zhí)行
                        .after(objAnim_scaleY)
                        .with(objAnim_translationX)  //和play動(dòng)畫(huà)同時(shí)執(zhí)行
                        .with(objAnim_translationY);
                animSet.setDuration(5000);
                animSet.start();
                break;
        }
    }
}

栗子很簡(jiǎn)單,算是做個(gè)自我鞏固以及方便日后回顧斥黑,如果不小心幫到別人了那自然也是極好的揖盘。最后附上Demo下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锌奴,一起剝皮案震驚了整個(gè)濱河市兽狭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖箕慧,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件服球,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡颠焦,警方通過(guò)查閱死者的電腦和手機(jī)斩熊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)伐庭,“玉大人粉渠,你說(shuō)我怎么就攤上這事∷朴牵” “怎么了渣叛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)盯捌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蘑秽,這世上最難降的妖魔是什么饺著? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮肠牲,結(jié)果婚禮上幼衰,老公的妹妹穿的比我還像新娘。我一直安慰自己缀雳,他們只是感情好渡嚣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著肥印,像睡著了一般识椰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上深碱,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天腹鹉,我揣著相機(jī)與錄音,去河邊找鬼敷硅。 笑死功咒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绞蹦。 我是一名探鬼主播力奋,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幽七!你這毒婦竟也來(lái)了景殷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滨彻,沒(méi)想到半個(gè)月后藕届,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亭饵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年休偶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜羊。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踏兜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出八秃,到底是詐尸還是另有隱情碱妆,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布昔驱,位于F島的核電站疹尾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骤肛。R本人自食惡果不足惜纳本,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腋颠。 院中可真熱鬧繁成,春花似錦、人聲如沸淑玫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)絮蒿。三九已至尊搬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歌径,已是汗流浹背毁嗦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留回铛,地道東北人狗准。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像茵肃,于是被迫代替她去往敵國(guó)和親腔长。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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