屬性動畫在Android自定義View中的應用場景

Android中動畫的類型,按照系統版本能夠簡單的分爲兩大類型,一種是傳統的動畫,也就是Android中最經常使用的View動畫,即幀動畫和補間動畫;另外一種是Android3.0之後支持的PropertyAnimation,即屬性動畫。這兩大類型的動畫雖然在實現一些動畫效果上有殊途同歸之處,但實現方式和使用場景仍是有較大的差異。android

View動畫

幀動畫和補間動畫是安卓中最多見的動畫,表現形式有AlphaAnimation(透明度動畫)、TranslateAnimation(位移動畫)、ScaleAnimation(縮放動畫)、RotateAnimation(旋轉動畫)、AnimationSet(動畫集合)以及幀動畫這些類型。能夠經過xml方式定義動畫動做,也能夠經過代碼方式定義動畫動做。以位移動畫爲例,這兩種動畫方式的典型使用方式以下:api

1.xml定義動畫

Step.1:在res/anim資源文件夾下建立translate動畫xml文件,代碼以下:bash

<translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="2000"
        android:fillAfter="true"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:repeatCount="3"
        android:toXDelta="0"
        android:toYDelta="100"/>
複製代碼

Step.2:代碼中引入xml動畫,設置到對應的View上並啓動動畫,關鍵代碼以下:app

Animation translateAnimation = AnimationUtils.loadAnimation(MainActivity.this,R.anim.translation);
imageView.setAnimation(translateAnimation);
imageView.startAnimation(translateAnimation);
複製代碼

以上爲xml聲明動畫的典型使用方式。ide

2.代碼聲明動畫

代碼聲明動畫的步驟相對xml聲明動畫的方式,不須要額外再res/anim中聲明動畫xml文件,而是經過代碼的方式直接聲明動畫及播放動畫時的相關動做,雖然步驟貌似比xml文件更少,可是代碼中聲明動畫相對於xml聲明動畫會更加複雜一下。代碼以下:函數

TranslateAnimation translateAnimation1 = new TranslateAnimation(TranslateAnimation.ABSOLUTE, mIvImg.getWidth(), TranslateAnimation.ABSOLUTE, mIvImg
                .getWidth() * 2f, TranslateAnimation.RELATIVE_TO_SELF, 0f, TranslateAnimation.RELATIVE_TO_SELF, 0);
//設置動畫持續時長
translateAnimation1.setDuration(3000);
//設置動畫結束以後的狀態是不是動畫的最終狀態,true,表示是保持動畫結束時的最終狀態
translateAnimation1.setFillAfter(true);
//設置動畫結束以後的狀態是不是動畫開始時的狀態,true,表示是保持動畫開始時的狀態
translateAnimation1.setFillBefore(true);
//設置動畫的重複模式:反轉REVERSE和從新開始RESTART
translateAnimation1.setRepeatMode(ScaleAnimation.REVERSE);
//設置動畫播放次數
translateAnimation1.setRepeatCount(ScaleAnimation.INFINITE);
//開始動畫
mIvImg.startAnimation(translateAnimation1);
複製代碼

對於幀動畫和補間動畫,對於Android開發者應該是很是熟悉,在此對使用方式和相關的api不作過多介紹,後續會分析幀動畫/補間動畫和屬性動畫的不一樣之處及各自的使用場景等。post

屬性動畫

1.什麼是屬性動畫?

屬性動畫是Android3.0之後系統提供的另一種「動畫」方式,這種「動畫」方式並非直接對頁面或者相關的View展現動態效果,它適用的對象也不侷限在View上,能夠理解爲它可使用在任何有「漸進」數據變化的對象上面,它做用的也不是對象自己而是對象中的相關提供了Getter和Setter的屬性。因此嚴格意義上來講,PropertyAnimation並不是是一個View動畫支持庫,而是數據在t秒內從x轉變成y的過程數據漸進變動庫。 正由於PropertyAnimator並不是嚴格意義上的View動畫支持庫,它能夠監控一個屬性在特定時間內的「演進過程」,就能夠作一些View動畫(幀動畫/補間動畫)作不到的事情,在實現特定的動畫效果上也有很是優秀的表現能力。動畫

2.屬性動畫的分類

屬性動畫有ObjectAnimator、ValueAnimator以及AnimatorSet三種實現形式。在使用方式上,屬性動畫和View動畫同樣有xml聲明和代碼聲明兩種方式。ui

3.經常使用語法及API

3.1 xml聲明動畫this

xml聲明動畫的經常使用用法以下:

<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>
</set>
複製代碼

其中,標籤、標籤、標籤分別對應着AnimatorSet、ObjectAnimator和ValueAnimator三種類型,這三者直接的關係主要是從屬關係和繼承關係,AnimatorSet是一個動畫集,能夠包含若干個動畫並操做這些動畫的執行順序等。ObjectAnimator是ValueAnimator的子類,在使用上比ValueAnimator更方便,但同時也會屏蔽到ValueAnimator內部實現和數據演進過程。

3.2 代碼聲明動畫

代碼聲明動畫並設置到View上實現動畫效果,以ObjectAnimator爲例,最簡單也是最典型的代碼以下:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 500f);
animator.setDuration(trasitionDuration);
animator.start();
複製代碼

ObjectAnimator支持鏈式變成,因此代碼能夠直接精簡成一行,以下:

ObjectAnimator.ofFloat(view, "translationX", 0f, 500f)
            .setDuration(500)
            .start();

複製代碼

這段代碼能夠實現將View在水平方向上500毫秒內從x=0的位置移動到x=500的位置的動畫。這個效果使用補間動畫中的位移動畫也能夠實現,可是實現方式較ObjectAnimator會略複雜,不論是xml聲明的方式仍是代碼聲明的方式。 下面簡單介紹下ObjectAnimator、ValueAnimator以及AnimatorSet類中經常使用的一些API。

ObjectAnimator

(1)public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
    這個是ObjectAnimator初始化入口之一,還有ofFloat/ofInt/ofArgb/ofMultiInt等入口,且ObjectAnimator不支持new ObjectAnimator來初始化對象,由於構造函數是私有的。
參數說明:
  ①target:該動畫做用的對象,類型是Object而不是View,說明ObjectAnimator並不只僅是爲了實現View的動畫效果而設計的。
  ②propertyName:屬性動畫做用的屬性名稱,通常是對象內提供了Getter和Setter方法的屬性,上面代碼的"translationX"就是View類中提供了Getter和Setter方法的屬性名,類型的屬性名還有"translationY""translationZ"等。
  ③values:爲float類型的不定參,通常是傳入兩個數值,第一個數值爲動畫開始時propertyName屬性對應的數值,第二個參數爲動畫結束時propertyName屬性對應的數值,也就是動畫做用的屬性的開始和結束的數值。

(2)public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
  參數說明見(1),不一樣之處就是須要傳入int類型的不定參數。

(3)public ObjectAnimator setDuration(long duration)
   設置動畫時長。

(4)public void setEvaluator(TypeEvaluator value)
   設置動畫插值器

(5)public void start()
   啓動動畫
複製代碼

ValueAnimator

(1)public static ValueAnimator ofInt(int... values)
   動畫初始化入口函數,入參爲動畫的開始和結束的數值,相似的入口函數還有ofFloat/ofArgb等等。

(2)public ValueAnimator setDuration(long duration)
   設置動畫時長

(3)public void setEvaluator(TypeEvaluator value)
   設置動畫插值器

(4)public void addUpdateListener(AnimatorUpdateListener listener)
   設置動畫監聽回調,能夠在回調中監聽動畫的執行過程,以便在動畫執行過程當中進行一些除動畫之外的操做。

(5)public void setRepeatCount(int value)
   設置動畫執行的次數

(6)public void setRepeatMode(@RepeatMode int value)
   設置動畫重複的模式,能夠設置從新開始動畫或者反轉動畫模式。

(7)public void start()
   啓動動畫
複製代碼

AnimatorSet

AnimatorSet本質是一個屬性動畫的容器,它能夠設置多個屬性動畫做爲自身動畫效果的一部分,並能夠設置這些屬性動畫是同時執行仍是順序執行。
(1)public AnimatorSet()
   構造函數,用來建立AnimatorSet對象

(2)public Builder play(Animator anim)
   播放一個屬性動畫

(3)public void playTogether(Animator... items)
   同時播放多個屬性動畫

(4)public void playSequentially(Animator... items)
   順序執行多個屬性動畫

(5)public AnimatorSet setDuration(long duration)
   設置動畫時長

(6)public void start()
   開始執行AnimatorSet中的動畫
複製代碼

4.屬性動畫在開發中的使用

4.1 Loading動畫效果

上圖爲點我達騎手曾經使用過的Loading頁面動效,這種動效已經超出了常規動畫(幀動畫/補間動畫)可以實現的效果範圍,這時候屬性動畫就要上場表演了!

簡單的整理下實現思路,中間那個相似水波紋同樣的有漸變顏色的圓圈,實際上是須要肯定兩個因素,一個是圓圈的半徑,另外一個就是圓圈的顏色的透明度。也就是在使用屬性動畫的過程當中,須要同時監聽半徑和顏色兩個屬性的數值,而後實時繪製圓圈並重復執行該動畫集合,就能夠實現這種特效。主要代碼以下:

public void playAnimation() {
    if (animatorSet == null) {
        animatorSet = new AnimatorSet();
        animatorSet.start();
        //圓圈半徑變動動畫
        ValueAnimator anim1 = ValueAnimator.ofInt(0, backBitmap.getWidth() / 2);
        //圓圈顏色透明度變動動畫
        ValueAnimator anim2 = ValueAnimator.ofInt(256, 0);
        anim1.setRepeatMode();
        animatorSet.playTogether(
                anim1, anim2
        );

        anim1.setRepeatCount(ValueAnimator.INFINITE);
        anim2.setRepeatCount(ValueAnimator.INFINITE);

        anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (animation != null) {
                    //圓圈半徑
                    animCircleRadius = (int) animation.getAnimatedValue();
                }
            }
        });
        anim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (animation != null) {
                    //圓圈透明度
                    currentOpacity = (int) animation.getAnimatedValue();
                }
            }
        });
    }

    animatorSet.setDuration(2000).start();

    if (hander == null)
        hander = new Handler();

    hander.postDelayed(circleAnimRunnable, 50);
}
private Runnable circleAnimRunnable = new Runnable() {
    @Override
    public void run() {
        animCircleColor = Color.argb(currentOpacity, 255, 143, 5);
        gradientPaint.setColor(animCircleColor);

        postInvalidate();
        //50毫秒刷新一次
        hander.postDelayed(this, 50);
    }
};
複製代碼

播放動畫的過程,其實就是監聽圓圈半徑和顏色透明度漸變的過程,在當前時刻圓圈的半徑和顏色透明度就是固定的,而後再使用Canvas實時繪製圓圈就能夠了。

4.2 展開-關閉效果

上圖是商家發單頁面最新的交互效果,點擊更換動畫的方式展開圖標,而後選中圖標後以動畫的方式回收圖標。這種動效很常見,貌似使用普通的位移動畫也能夠很輕鬆實現,但使用位移動畫實現這種動效並支持圖標的點擊交互並無想象中的容易。

上圖中,當View從A位置經過位移動畫移動到B位置時,在沒有交互的狀況下是能夠知足動畫效果的,可是若是View有一些點擊動做的交互,這時候使用位移動畫就會有一個很是嚴重的問題存在,就是View從A雖然」移動「到了B的位置,可是View的點擊區域還停留在A的位置,這時候你在B的位置點擊View是響應不到View的點擊事件的。

爲了解決位移動畫在這種場景下使用時出現的問題,大概的思路有兩種,一種就是使用位移動畫移動到B位置時,在B的位置克隆一份完整的View放到該位置而後將原來的View設置GONE。另一種,就是經過位移動畫的方式將View從A移動到B時,B位置的點擊事件是能夠響應的。顯而易見,使用屬性動畫在View有交互的場景下更合適。

4.3 View翻轉效果

這種View的翻轉效果使用常規動畫是無從下手的,但使用屬性動畫一行代碼就能夠輕鬆實現。

ObjectAnimator.ofFloat(imageView, "rotationY", 0f, 360f)
            .setDuration(1000)
            .start();
複製代碼

View動畫 vs 屬性動畫

1.View動畫

  • View動畫只能爲View添加動畫效果,且不能監聽View相關屬性的變化過程。
  • View動畫提供的動畫能力較爲單一,目前只支持幀動畫、縮放動畫、位移動畫、旋轉動畫、透明度動畫以及這些動畫的集合動畫。
  • View動畫改變的是View的繪製效果,View的真正位置和相關屬性並不會改變,這也就形成了點擊事件的觸發區域是動畫前的位置而不是動畫後的位置的緣由。

2.屬性動畫

  • 屬性動畫做用對象不侷限在View上,而是任何提供了Getter和Setter方法的對象的屬性上。
  • 屬性動畫沒有直接改變View狀態的能力,而是經過動態改變View相關屬性的方式來改變View的顯示效果。
  • 屬性動畫使用更方便,能夠用更簡潔的代碼實現相關的動畫效果。
  • 屬性動畫上手難度較高,對於propertyName須要本身去挖掘,或者本身經過Wrapper的方式去自定義propertyName。
  • 屬性動畫是Android3.0以上系統提供的能力,在3.0如下需導入nineoldandroids三方庫解決兼容性問題。

小結:

屬性動畫是功能更強大、實現方式更優雅的動畫解決方案,在爲自定義View設置動效上有着很是強大的表現能力,能夠實現View動畫實現不了的更加炫酷的動畫效果。自定義動畫是Android開發者的基本功,配合屬性動畫在實現一些動效上能夠達到事半功倍的效果,也是Android開發者必須掌握的一貫基本技能。

相關文章
相關標籤/搜索