屬性動畫之ObjectAnimator

    ObjectAnimator,通過設置改變對象的屬性來實現動畫效果,常用的方法有這麼幾種,ofFloat()、ofInt()、ofObject()、ofArgb()、ofPropertyValuesHolder(),具體含義及使用我們在下面的實例中進行講解。

一、動畫類型
    使用ObjectAnimator也是可以輕鬆的實現平移、縮放、旋轉、透明度這幾種動畫效果的,與補間動畫的使用效果是一樣的,那就先來看看這幾種常用的動畫是怎麼實現的。

    工程代碼裏就是一個ImageView控件和Activity,

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

<ImageView
    android:id="@+id/head"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:src="@drawable/head" />

</RelativeLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_object_animator);   
        head = (ImageView) findViewById(R.id.head);
    }
    我們這裏就是對ImageView控件head實現動畫效果,本質就是改變head的屬性。
1.平移(translate)
// 平移
private void translateAnimation() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0.0f);
    objectAnimator.setDuration(2000);
    objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.setRepeatMode(Animation.RESTART);
    objectAnimator.start();
}
    這段代碼就是用來實現控件的平移,我們來逐行分析這段代碼。
    ofFloat方法的函數原型,
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
參數含義:
    target:動畫操作的對象,我們這裏的操作對象就是ImageView控件head
    propertyName:屬性名稱,這裏的"translationX"屬性值意思就是在水平方向移動,如果是"translationY"就是在垂直方向移動,下面講到的其動畫效果也是這個意思,這個屬性值跟我們在xml文件中設置屬性值得名稱是一致的,比如
"android:translationY"。
    values:動畫過渡值,過渡值可以有一個到N個,如果是一個值的話,就默認是這個動畫過渡值的結束值,如果有N個值,動畫就在這N個值之間過渡,如本例中有三個過渡值"0.0f, 350.0f, 0f",意思就是從當前位置向右滑到350的位置,再滑到位置0,即初始位置。
    然後是動畫的設置,
    objectAnimator.setDuration(2000);//動畫的時間間隔
    objectAnimator.setRepeatCount(Animation.INFINITE);//重複次數
    objectAnimator.setRepeatMode(Animation.RESTART);//重複模式
    最後start,動畫就開始執行了
     objectAnimator.start();
2.縮放(scale)
// 縮放
private void scaleXAnimation() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "scaleX", 1.0f, 1.5f);
    objectAnimator.setDuration(2000);
    objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.setRepeatMode(Animation.RESTART);
    objectAnimator.start();
}

3.旋轉(rotate)
// 旋轉
private void rotateAnimation() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "rotationX", 0.0f, 90.0f,0.0F);
    objectAnimator.setDuration(2000);
    objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.setRepeatMode(Animation.RESTART);
    objectAnimator.start();
}

4.透明度(alpha)
// 透明度
private void alphaAnimation() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "alpha", 1.0f, 0.3f, 1.0F);
    objectAnimator.setDuration(2000);
    objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.setRepeatMode(Animation.RESTART);
    objectAnimator.start();
}

    可能我們也注意到了,在縮放效果的實例中,圖片被放大後就保持當前的狀態,沒有變爲初始的樣子,這是因爲我們在設置過渡值是最終的狀態是1.5f,就放大到1.5倍,並沒有讓它回到原來的狀態,從這點就可以看出,屬性動畫是真真切切的可以改變控件的屬性的,這是與補間動畫的最大不同,補間動畫在動畫結束後都將回到初始狀態。所以說,屬性動畫的使用就顯得更加的靈活。至於上面的其他三個實例,最終回到初始狀態,是因爲我們在設過渡值的時候最終狀態設定的就是初始狀態。
    剛纔我們討論了幾種常見的動畫效果,如果僅有這些功能的話,那就與補間動畫沒有太大的區別。其實,屬性動畫還可以設置其他的屬性值。ObjectAnimator的ofObject方法,就是用來對任意對象進行動畫設置的,如字體顏色,直接看實例

<TextView
android:id="@+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:layout_centerInParent="true"
android:textSize="32sp"
android:text="測試顏色變化"/>
int startColor = 0xffff0000;
int endColor = 0xff00ff00;
ObjectAnimator objectAnimator4 = ObjectAnimator.ofObject(txt, "textColor", new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;

int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;

return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                (int)((startB + (int)(fraction * (endB - startB))));
    }
}, startColor, endColor);
objectAnimator4.setDuration(3000);
objectAnimator4.start();

    ofObject方法的原型
public static ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
    我們看到它比ofFloat多了一個TypeEvaluator參數,它是用來告知系統如何進行屬性值過渡的,由於ofObject是任意屬性,所以需要自己實現TypeEvaluator,ofFloat沒有該參數是因爲系統默認實現了其過渡行爲FloatEvaluator,關於TypeEvaluator我們會單獨講解,這邊就不在詳述了。
    除了字體顏色外,還可以設置背景色"backgroundColor"等。
    剛纔的字體顏色除了使用ofObject方法外,還可以使用ofArgb,但需要SDK版本21上支持,目前市面上很多機型還是21以下的,暫時我們最好不要使用。
ObjectAnimator objectAnimator2 = ObjectAnimator.ofArgb(txt, "textColor", 0x000, 0x00FF00);
objectAnimator2.start();

二、常用設置
    在上面的例子中,我們已經對動畫進行了一些設置,如間隔、重複次數等等,下面就將常見的設置羅列出來。
1.setInterpolator():設置動畫插值
    控制動畫的變化速率,系統中定義了好幾種Interpolator:
    LinearInterpolator--均勻的速率改變
    AccelerateDecelerateInterpolator--先加速後減速
    AccelerateInterpolator--加速
    DecelerateInterpolator--減速
    CycleInterpolator--動畫循環播放特定的次數,速率改變沿着正弦曲線
2.setDuration():設置動畫執行時間,動畫時間以毫秒爲單位(ms)
3.setRepeatCount():設置動畫重複次數
    大於0的值就代表重複幾次,如果需要無限循環,設爲-1,上面的Animation.INFINITE是系統給的常量,值爲-1,代表無限循環,我們建議使用這個常量,如果設爲0呢?也是執行一次。
4.setRepeatMode():設置動畫重複模式
5.setStartDelay():設置動畫延時操作,也是以毫秒爲單位(ms) 
6.setTarget():設置動畫的對象
    操作對象,上面的例子中將動畫對象通過ofXXX方傳遞,如果需要改變動畫對象,但動畫效果不變,我們可以使用該方法來設置。
objectAnimator.setTarget(txt);//將動畫對象head變爲txt
7.setEvaluator():設置動畫過度的評估者,即設置TypeEvaluator對象,後面會詳細介紹

三、組合動畫
    上面講到的都是單一的動畫,在實際使用時,有可能同時需要多種動畫效果,比如同時在水平方向和垂直方向進行縮放並且同時繞水平方向旋轉,要實現這樣的效果就必須用到組合動畫。可以有三種方式來實現,一一講解。
1.AnimatorSet
    AnimatorSet可以讓幾個動畫同時執行,也可以設置執行順序、延遲執行等,
    after(Animator anim)--將現有動畫插入到傳入的動畫之後執行
after(long delay)--將現有動畫延遲指定毫秒後執行
before(Animator anim)--將現有動畫插入到傳入的動畫之前執行
with(Animator anim)--將現有動畫和傳入的動畫同時執行
    playSequentially(Animator... items)--依次執行
private void multiAnimation() {
        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(head, "scaleX", 1.0f, 2.5f, 1.0f);
        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(head, "scaleY", 1.0f, 2.5f, 1.0f);
        ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(head, "rotationX", 0.0f, 90.0f,0.0F);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(objectAnimator1).with(objectAnimator2).with(objectAnimator3);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }

    讓動畫同時執行除了使用with方法外,還可以用playTogether方法,

animatorSet.playTogether(objectAnimator1, objectAnimator2, objectAnimator3);
    其他幾種就不在演示了。
2.PropertyValuesHolder
    這種方式只能多個動畫一起執行,不同設置先後順序。
private void multiAnimation2() {
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.5f, 1.0f);
        PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.5f, 1.0f);
        PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f,0.0F);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(head, valuesHolder, valuesHolder1, valuesHolder2);
        objectAnimator.setDuration(2000);
        objectAnimator.start();
}
3.ViewPropertyAnimator
    該類是多屬性動畫,從名字就可以看出該類的作用對象是view,當一個view對象需要同時執行多個屬性動畫的時候就可以考慮使用該類了。比如說:一個ImageView先右移動的同時進行放大一倍的動畫效果實現如下
private void viewPropertyAnimator() {
        ViewPropertyAnimator animator = head.animate();
        animator.translationX(200)
                .scaleX(2)
                .scaleY(2)
                .setDuration(2000)
                .start();
    }

四、動畫的監聽
    一般情況下,我們除了需要動畫效果外,還需要對動畫的執行過程進行監聽,在執行前、執行結束後或者執行過程中,做出相應的處理,比如動畫結束後,請求網絡數據。系統給我們提供幾種監聽接口,來監聽動畫的各個狀態,如AnimatorListener,
private void animationListener() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f);
        objectAnimator.setDuration(2000);
        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                Log.d("dingfeng", "onAnimationStart......");
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Log.d("dingfeng", "onAnimationEnd......");
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                Log.d("dingfeng", "onAnimationCancel......");
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                Log.d("dingfeng", "onAnimationRepeat......");
            }
        });
        objectAnimator.start();
    }
    可以根據不同需求來實現接口裏面的四個方法,可以根據需要做相應的處理。但有時候你會覺得我不需要監聽動畫的四種狀態,我只需要監聽動畫結束時候的狀態,使用上面的方法就會感覺代碼臃腫了,不過沒關係,Android系統給我們提供了一個更好用的方法,
private void animationListener2() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f);
        objectAnimator.setDuration(2000);
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.d("dingfeng", "onAnimationEnd......");
            }
        });
        objectAnimator.start();
    }
    可以看出,我們使用了AnimatorListenerAdapter動畫接口適配器代替AnimatorListener接口。其實AnimatorListenerAdapter的源碼只是一個實現了AnimatorListener接口的抽象類而已,
你需要監聽哪種動畫狀態就重寫哪種方法就可以了。
除此之外,AnimatorUpdateListener接口就可以讀取到動畫的每個更新值。
private void animationListener3() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f);
        objectAnimator.setDuration(2000);
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float)animation.getAnimatedValue();
                Log.d("dingfeng", "onAnimationUpdate......"+value);
            }
        });
        objectAnimator.start();
    }
一般情況下不太用到這個接口,但是在自定義動畫的時候,通過該接口,可以實現很多複雜的效果。

五、xml編寫動畫
    當然,我們也可以在xml文件中配置動畫,這樣做可以比較好的複用這些動畫,減少代碼量。
1.在資源目錄下創建animator文件夾
    將編寫的xml文件放在該目錄下,如下圖

    我們創建兩個動畫文件,一個是旋轉動畫,一個組合動畫

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android='http://schemas.android.com/apk/res/android'
android:duration='2000'
android:propertyName='rotationX'
android:repeatCount="-1"
android:repeatMode="restart"
android:valueFrom='0'
android:valueTo='180'
android:valueType='floatType' />
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android='http://schemas.android.com/apk/res/android'
android:duration='2000'
android:ordering='sequentially'><!--動畫執行順序 sequentially:順序執行;together:同時執行。 -->
<objectAnimator
android:propertyName='translationX'
android:valueFrom='0'
android:valueTo='200'
android:valueType='floatType' />

    <set android:ordering='together'>
        <objectAnimator
        android:propertyName='scaleX'
        android:valueFrom='1'
        android:valueTo='2'
        android:valueType='floatType' />
        <objectAnimator
        android:propertyName='scaleY'
        android:valueFrom='1'
        android:valueTo='2'
        android:valueType='floatType' />
    </set>
</set>
2.加載動畫
    使用AnimatorInflater加載動畫文件
head = (ImageView) findViewById(R.id.head);
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.multi);
animator.setTarget(head);
animator.start();
    這樣就OK了,還是很容易的。

    下一章再說說ValueAnimator 

發佈了35 篇原創文章 · 獲贊 10 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章