Android動畫使用之屬性動畫

1. 前言

這是動畫的第三篇屬性動畫,屬性動畫是android動畫裏面用的最多的, 也是最重要的。android中三種動畫的使用文章以下:java

  1. 幀動畫
  2. 補間動畫
  3. 屬性動畫

2. 介紹
  • 屬性動畫是指經過不斷的改變對象的屬性,進行動畫。 其中涉及到三個類:ValueAnimator、ObjectAnimator、ViewPropertyAnimator 的使用。
  • 屬性動畫能做用於任何對象, 不像補間動畫同樣, 只能做用在view上。 總結來講就是屬性動畫能夠經過改變任何對象的任何屬性來操做動畫。

3. 使用
3.1 ValueAnimator使用

先來一張動畫效果圖android

image

3.1.1 ValueAnimator 介紹
  • ValueAnimator是動畫中最重要的一個類,後面要學的ObjectAnimator也是繼承於它實現的。
  • ValueAnimator,從名字就能夠看出,這是一個經過值來操做動畫的類。 咱們只須要指定一個開始值和結束值,那麼這個類就會自動幫咱們計算好, 在指定的動畫時間內, 每一個時間點對應的值是多少。
3.1.2 ofInt、ofFloat、ofArgb

經過調用ValueAnimator.ofInt這個方法, 咱們就能構建一個整型的ValueAnimator,以下:git

private void valueAnimatorOfInt() {
        //獲取到屏幕寬度
        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;
        //起始值爲btn最開始的座標的x位置, 結束值爲 屏幕的最右邊,
        ValueAnimator valueAnimator = ValueAnimator.ofInt((int) btnOfInt.getTranslationX(), widthPixels);

        valueAnimator.setDuration(2000);
        valueAnimator.setRepeatCount(2);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.setStartDelay(500);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int currentValue = (int) valueAnimator.getAnimatedValue();
                btnOfInt.setTranslationX(currentValue);
                System.out.println("current value:" + currentValue);

            }
        });
        valueAnimator.start();
    }

複製代碼

解釋:github

  • ValueAnimator.ofInt: 傳入一個int類型的開始值和結束值,構建動畫對象。
  • valueAnimator.setDuration:設置動畫時間
  • valueAnimator.setRepeatCount: 設置動畫重複次數。
  • valueAnimator.setRepeatMode:設置動畫重複模式, 正序重複或者是逆序重複。
  • valueAnimator.setStartDelay:設置動畫開始後的延遲執行時間。
  • valueAnimator.addUpdateListener: 設置動畫監聽器,監聽數值的變化,獲取到當前的數值,而後去設置view的屬性。這裏設置的是view的x位置。效果如上面的gif圖。
  • valueAnimator.start:啓動動畫

使用十分簡單,ValueAnimator.ofFloat,ValueAnimator.ofArgb 都是同理,傳入開始值和結束值, 而後監聽數值的變化, 設置給你要進行動畫的view。 下面定義了兩個方法,是對ofFloat,ofArgb的使用:canvas

private void valueAnimatorOfFloat() {
        //獲取到屏幕寬度
        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;


        //起始值爲btn最開始的寬度, 結束值爲 屏幕的寬度。
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(btnofFloatWidth,widthPixels);

        valueAnimator.setDuration(2000);
//        valueAnimator.setRepeatCount(2);
//        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
//        valueAnimator.setStartDelay(500);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float currentValue = (float) valueAnimator.getAnimatedValue();
                btnOfFloat.getLayoutParams().width = (int) currentValue;
                //從新繪製
                btnOfFloat.requestLayout();
                System.out.println("current value:" + currentValue);
            }
        });
        valueAnimator.start();
    }


    private void valueOfRGB(){
        ValueAnimator valueAnimator = ValueAnimator.ofArgb(Color.BLUE, Color.RED);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int currentValue = (int) valueAnimator.getAnimatedValue();
                btnOfRgb.setBackgroundColor(currentValue);
                System.out.println("current value:" + currentValue);
            }
        });
        valueAnimator.start();
    }

複製代碼
3.1.3 ValueAnimator.ofObject

在介紹中說過, 屬性動畫能夠做用在任意對象。 好比我有一個Point類,類中有x,y兩個屬性用來表示座標。 經過Point建立兩個對象,一個pointA,PointB。如何從pointA變化到pointB呢?bash

public class MyView extends View {

    private float RADIUS = 50f;

    private Point currentPoint;

    private Paint mPaint ;

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
        currentPoint = new Point(RADIUS,RADIUS);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircle(canvas);
    }

    private void drawCircle(Canvas canvas){
        float x = currentPoint.getX();
        float y = currentPoint.getY();
        canvas.drawCircle(x,y,RADIUS,mPaint);
    }

    public void startAnimation (){
        Point startPoint = new Point(RADIUS, RADIUS);
        Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        valueAnimator.setDuration(5000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Point point = (Point)valueAnimator.getAnimatedValue();
                currentPoint = point;
                invalidate();

                System.out.println("current point,x:"+currentPoint.getX()+"y:"+currentPoint.getY());
            }
        });
        valueAnimator.start();

    }

}
複製代碼

自定義了一個view畫一個圓。startAnimation方法可使這個圓從startPoint移動到endPoint。dom

解釋ide

  • ValueAnimator.ofObject: 經過ofObject建立一個動畫對象, 傳入估值器、開始對象和結束對象。post

  • 估值器是個什麼東西?
    估值器就是用來描述動畫邏輯的,說白了就是計算在對應的時間點上, 當前的值是多少。 以前的ofInt、ofFloat、ofArgb 這些, 系統都幫咱們寫好了,計算好了。 可是若是是從一個任意對象變化到另外一個對象。 就須要咱們本身寫估值器了。動畫

/**
 * point 估值器
 */
public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        return point;
    }
}

複製代碼

實現TypeEvaluator接口就好了, 接口會傳當前動畫進度、開始對象和結束對象進來。 你只須要根據這三個東西來計算出當前動畫進度的對象並返回就能夠了。

3.1.4 PropertyValuesHolder

上面都是對一個屬性變化進行動畫, 若是是多個呢,好比我要改一個按鈕的寬和高。 那就要用到PropertyValuesHolder。

private void valueOfPropertyValues(){

        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;
        int heightPixels = displayMetrics.heightPixels;

        PropertyValuesHolder holder1 = PropertyValuesHolder.ofInt("width", btnWidth, widthPixels);
        PropertyValuesHolder holder2 =PropertyValuesHolder.ofInt("height",btnHeight,heightPixels-btnOfValuesProperty.getTop());

        ValueAnimator valueAnimator = ValueAnimator.ofPropertyValuesHolder(holder1, holder2);
        valueAnimator.setDuration(5000);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int width = (int) valueAnimator.getAnimatedValue("width");
                int height = (int) valueAnimator.getAnimatedValue("height");

                btnOfValuesProperty.getLayoutParams().width = width;
                btnOfValuesProperty.getLayoutParams().height = height;
                btnOfValuesProperty.requestLayout();

                System.out.println("width:"+width+"height:"+height);
            }
        });

        valueAnimator.start();
    }

複製代碼

解釋:

  • PropertyValuesHolder.ofInt: 和剛剛的ValueAnimator.ofInt 同樣, 都是傳入一個開始值和結束值。建立出來一個要變化的屬性對象。

  • ValueAnimator.ofPropertyValuesHolder: 把建立的多個要變化的屬性對象傳進來,構建出一個動畫對象。

  • valueAnimator.getAnimatedValue: 經過屬性名, 獲取到屬性的當前值。拿到多個屬性值後, 咱們就能夠來從新設置按鈕的寬高造成動畫啦。

3.1.4 ValueAnimator使用總結

咱們傳入一個開始值和結束值給到ValueAnimator, 它會幫咱們計算出在當前時間,這個值是多少。 咱們經過監聽獲取到這個值,從新設置給view等對象,讓view重繪。這就是ValueAnimator作動畫的原理。

3.2 ObjectAnimator使用

上面說的ValueAnimator咱們是經過監聽值的變化來給view設置屬性,從新繪製,完成動畫。 那麼ObjectAnimator就是咱們把屬性名給它,讓他本身來根據屬性值的變化完成動畫。

image

3.2.1 translate、rotate、scale、alpha

好比咱們要經過ObjectAnimator來改變view的屬性,讓view平移、選擇、縮放、透明:

private void translate() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnTranslate, "translationX", 0, 500, 200);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void rotate() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnRotate, "rotation", 0, 200, 300);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void scale() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnScale, "ScaleX", 0f, 0.5f, 1.1f);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void alpha() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnAlpha, "Alpha", 0, 0.5f, 1f);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }
複製代碼

解釋:

  • ObjectAnimator.ofFloat: 傳入要動畫的對象,和對象動畫要變化的屬性名,屬性的開始值和結束值。 ofInt、ofArgb等同理。
3.2.2 自定義屬性

上面那些是view本地有自帶的setTranslationX、setRotation等方法,才能夠經過translationX、rotataion這些屬性來完成動畫。 若是咱們自定義的view,要變化其餘屬性呢,好比一個進度條, 經過變化進度屬性值來完成進度的動畫。那就須要咱們本身定義屬性和屬性對應的setter方法,並在setter方法中,重繪view。

  1. 先自定義view,給progress添加setter方法,在setter中重繪。
public class ProgressView extends View {

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private float progress = 0;

    private float RADIUS = 80;


    public ProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    public float getProgress() {
        return progress;
    }

    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float centerX = getWidth()/2;
        float centerY = getHeight()/2;


        mPaint.setColor(Color.RED);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(25);
        mPaint.setStyle(Paint.Style.STROKE);
        RectF rectF = new RectF(centerX - RADIUS, centerY - RADIUS, centerX + RADIUS, centerY + RADIUS);

        canvas.drawArc(rectF,0,3.6f*progress,false,mPaint);

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText((int) progress + "%", centerX, centerY - (mPaint.ascent() + mPaint.descent()) / 2, mPaint);


    }
}

複製代碼
  1. 變化屬性值,完成動畫。
private void custom() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(progressView, "progress", 0, 90);
        objectAnimator.setDuration(5000);
        objectAnimator.setInterpolator(new FastOutSlowInInterpolator());
        objectAnimator.start();


        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {

            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        objectAnimator.addListener(new AnimatorListenerAdapter() {
// @Override
// public void onAnimationStart(Animator animation) {
// super.onAnimationStart(animation);
// }
        });
    }

複製代碼

這樣咱們就能夠對咱們自定義view中的屬性變化並完成動畫了。

3.2.2 指定動畫的關鍵幀

這裏的關鍵幀就是用來指定,在某一幀的時候的屬性。 好比動畫到一半時候的那一幀, 我要把屬性設置成什麼什麼。下面例子仍是用上面那個進度條, 當動畫到一半的時候,把進度設置成100。

private void keyFrame() {
        //設置關鍵幀, 第一個參數是 完成度, 第二個參數是 屬性值, 好比當完成度一半的時候(0.5),屬性值(progress)給100;
        Keyframe keyframe = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe keyframe2 = Keyframe.ofFloat(1, 80);
        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe, keyframe1, keyframe2);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(progressView, holder);

        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }
複製代碼

解釋:

  • Keyframe.ofFloat: 設置一個關鍵幀,傳入動畫進度,和屬性值。
  • PropertyValuesHolder.ofKeyframe:經過這些關鍵幀來建立多屬性的對象。
  • ObjectAnimator.ofPropertyValuesHolder: 經過多屬性對象來建立動畫對象。
3.3 ViewPropertyAnimator使用

ViewPropertyAnimator, 是直接經過view的animate來對view的一些自帶屬性進行動畫。 animate返回的對象就是ViewPropertyAnimator類型。

private void viewProperty(){
       btnViewProperty.animate().alpha(0).setDuration(2000).rotation(360).translationX(300);
    }
複製代碼
3.4 組合動畫

不少時候, 單個動畫知足不了需求。 須要將多個動畫變成一連串的組合動畫。 好比講移動、旋轉、透明組合起來, 一塊兒或者前後完成動畫。

private void animatorSet() {

//        AnimatorSet.playTogether(Animator... anim) :  將動畫組合一塊兒執行
//        AnimatorSet.playSequentially(Animator... anim) :  將動畫組合有序執行


//        AnimatorSet.play(Animator anim)   :播放當前動畫
//        AnimatorSet.after(long delay)   :將現有動畫延遲x毫秒後執行
//        AnimatorSet.with(Animator anim)   :將現有動畫和傳入的動畫同時執行
//        AnimatorSet.after(Animator anim)   :將現有動畫插入到傳入的動畫以後執行
//        AnimatorSet.before(Animator anim) :  將現有動畫插入到傳入的動畫以前執行


        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;

        ObjectAnimator translationX = ObjectAnimator.ofFloat(btnSet, "translationX", 0, widthPixels);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(btnSet, "rotation", 0, 360);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(btnSet, "alpha", 1, 0, 1);

        AnimatorSet animatorSet = new AnimatorSet();

//        animatorSet.playTogether(translationX,rotation,alpha);
//        animatorSet.playSequentially();

        animatorSet.play(rotation).with(alpha).before(translationX);

        animatorSet.setDuration(5000);
        animatorSet.setInterpolator(new LinearInterpolator());
        animatorSet.start();

    }
複製代碼

解釋:

  • AnimatorSet: 組合動畫類
  • AnimatorSet.playTogether:將動畫組合一塊兒執行
  • AnimatorSet.playSequentially: 將動畫組合有序執行
  • AnimatorSet.play:播放當前動畫
  • AnimatorSet.after:將現有動畫延遲x毫秒後執行
  • AnimatorSet.with(Animator:將現有動畫和傳入的動畫同時執行
  • AnimatorSet.after:將現有動畫插入到傳入的動畫以後執行
  • AnimatorSet.before:將現有動畫插入到傳入的動畫以前執行
3.5 xml中編寫動畫

在/res/animator/目錄下新建xml文件:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially" >

    <objectAnimator android:duration="2000" android:propertyName="translationX" android:valueFrom="-200" android:valueTo="0" android:valueType="floatType" >
    </objectAnimator>

    <set android:ordering="together" >
        <objectAnimator android:duration="3000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" >
        </objectAnimator>

        <set android:ordering="sequentially" >
            <objectAnimator android:duration="1500" android:propertyName="alpha" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" >
            </objectAnimator>
            <objectAnimator android:duration="1500" android:propertyName="alpha" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" >
            </objectAnimator>
        </set>
    </set>

</set>
<!-- 將一個視圖先從屏幕外移動進屏幕,而後開始旋轉360度,旋轉的同時進行淡入淡出操做-->

複製代碼

xml中使用的比較少,這裏也不詳細寫。能夠參考下面郭霖文章寫的。


4.總結
  • 屬性動畫能夠做用在任意對象,任意屬性。
  • 屬性動畫更改的是真實的對象的屬性, 不像補間動畫同樣,沒有更改對象屬性。
  • 屬性動畫的使用主要涉及到三個類:ValueAnimator、ObjectAnimator、ViewPropertyAnimator。
  • ValueAnimator和ObjectAnimator的原理都是經過不斷改變對象的屬性後重繪對象。來使對象完成動畫。 ValueAnimator須要咱們本身重繪,ObjectAnimator是自動重繪。
  • 屬性動畫能夠操做多個屬性、關鍵幀、組合動畫等。
  • 系統View的對象還提供了ViewPropertyAnimator讓咱們直接操做view自帶的一些屬性的動畫。
  • xml編寫動畫的優勢是可讀性、複用性高。缺點是靈活性差。

5. 完整demo地址
6. 參考文章

7. 歷史文章目錄
相關文章
相關標籤/搜索