天天看點

Android中的屬性動畫

文章目錄

    • 1.ValueAnimator
      • 概述
      • 估值器
      • ValueAnimator.ofObject使用的小demo
    • 2.ObjectAnimator類
      • 常用的屬性值:
      • 一個沿x軸平移的小demo
      • 使用ObjectAnimator.ofObject實作ValueAnimator.ofObject的相同效果
    • 3.AnimatorSet

源碼位址

1.ValueAnimator

概述

ValueAnimator不提供任何動畫效果,它更像一個數值發生器,用來産生有一定規律的數字,進而讓調用者控制動畫的實作過程.

估值器

  • 估值器(TypeEvaluator)決定 值 的具體變化數值
  • ValueAnimator有3個比較常用的方法:ValueAnimator.ofInt(int values),ValueAnimator.ofFloat(float values)和ValueAnimator.ofObject(int values);其中ofInt内置了整形估值器IntEvaluator,ofFloat内置了浮點型估值器FloatEvaluator,ofObject沒有預設的估值器

FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>float</code> or
     *                   <code>Float</code>
     * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        // fraction:表示動畫完成度(根據它來計算目前動畫的值)
        // startValue、endValue:動畫的初始值和結束值
        float startFloat = startValue.floatValue();
         // 傳回對象動畫過渡的邏輯計算後的值
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}
           

IntEvaluator

public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>int</code> or
     *                   <code>Integer</code>
     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}
           

ValueAnimator.ofObject使用的小demo

Android中的屬性動畫

自定義MyPoint類:

public class MyPoint {
    // 設定兩個變量用于記錄坐标的位置
    private float x;
    private float y;

    // 構造方法用于設定坐标
    public MyPoint(float x, float y) {
        this.x = x;
        this.y = y;
    }

    // get方法用于擷取坐标
    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

           

自定義估值器PointEvaluator:

public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // 将動畫初始值startValue 和 動畫結束值endValue 強制類型轉換成Point對象
        MyPoint startPoint = (MyPoint) startValue;
        MyPoint endPoint = (MyPoint) endValue;

        // 根據fraction來計算目前動畫的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());

        // 将計算後的坐标封裝到一個新的Point對象中并傳回
        MyPoint point = new MyPoint(x, y);
        return point;
    }
}

           

自定義圓圈CriView

public class CriView extends View{
    public static final float RADIUS = 70f;// 圓的半徑 = 70
    public MyPoint currentPoint;// 目前點坐标
    private Paint mPaint;// 繪圖畫筆

    public CriView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 初始化畫筆
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 如果目前點坐标為空(即第一次)
        if (currentPoint == null) {
            // 建立一個點對象(坐标是(70,70))
            currentPoint = new MyPoint(RADIUS, RADIUS);
            // 在該點畫一個圓:圓心 = (70,70),半徑 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }else{
            // 如果坐标值不為0,則畫圓
            // 是以坐标值每改變一次,就會調用onDraw()一次,就會畫一次圓,進而實作動畫效果
            // 在該點畫一個圓:圓心 = (30,30),半徑 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }

    public void setPonit(MyPoint ponit) {
        this.currentPoint = ponit;
    }
}

           

xml檔案使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.test.ck.propertyanimator.MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="變化" />
    <com.test.ck.propertyanimator.CriView
        android:id="@+id/criView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

           

activity内的點選事件:

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始點為圓心(70,70)
                MyPoint endPoint = new MyPoint(200, 250);// 結束點為(200, 250)
                ValueAnimator va = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
                va.setDuration(3000);
                va.start();
                va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        MyPoint animatedValue = (MyPoint) animation.getAnimatedValue();
                        criView.setPonit(animatedValue);
                        //requestLayout方法隻會導緻目前view的measure和layout,而draw不一定被執行,
                        // 隻有當view的位置發生改變才會執行draw方法,是以如果要使目前view重繪需要調用invalidate
//                        criView.requestLayout();
                        criView.invalidate();
                    }
                });
            }
        });
           

2.ObjectAnimator類

常用的屬性值:

(ObjectAnimator是繼承ValueAnimator的)

• translationX和translationY:用來沿着X軸或者Y軸進行平移。

• rotation、rotationX、rotationY:用來圍繞View的支點進行旋轉。

• PrivotX和PrivotY:控制View對象的支點位置,圍繞這個支點進行旋轉和縮放變換處理。預設該支點位置就是View對象的中心點。

• alpha:透明度,預設是1(不透明),0代表完全透明。

• x和y:描述View對象在其容器中的最終位置

一個沿x軸平移的小demo

Android中的屬性動畫
button.setOnClickListener(new View.OnClickListener() {
            /**
             * @param v
             *  ObjectAnimator.ofFloat(Object object, String property, float ....values);
             *  參數說明
             *  object 需要操作的對象
             *  property 需要操作的對象的屬性(這個屬性必須要有get和set方法)
             *   float ....values:動畫初始值 & 結束值(不固定長度)
             */
            @Override
            public void onClick(View v) {
                ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                //設定動畫時長
                oa.setDuration(2000);
                //設定動畫延遲播放時間
                oa.setStartDelay(500);
                // 設定動畫重複播放次數 = 重放次數+1 動畫播放次數 = infinite時,動畫無限重複
                oa.setRepeatCount(1);
                // 設定重複播放動畫模式 ValueAnimator.RESTART(預設):正序重放;ValueAnimator.REVERSE:倒序回放
                oa.setRepeatMode(ValueAnimator.REVERSE);
                oa.start();
            }
        });
           

使用ObjectAnimator.ofObject實作ValueAnimator.ofObject的相同效果

Android中的屬性動畫

隻要需要在自定義的CriView中添加currentPoint屬性的get和set方法,并且在set方法中調用 invalidate()方法

public class CriView extends View{
    public static final float RADIUS = 70f;// 圓的半徑 = 70
    public MyPoint currentPoint;// 目前點坐标
    private Paint mPaint;// 繪圖畫筆

    public CriView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 初始化畫筆
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 如果目前點坐标為空(即第一次)
        if (currentPoint == null) {
            // 建立一個點對象(坐标是(70,70))
            currentPoint = new MyPoint(RADIUS, RADIUS);
            // 在該點畫一個圓:圓心 = (70,70),半徑 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }else{
            // 如果坐标值不為0,則畫圓
            // 是以坐标值每改變一次,就會調用onDraw()一次,就會畫一次圓,進而實作動畫效果
            // 在該點畫一個圓:圓心 = (30,30),半徑 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }

    public void setPonit(MyPoint ponit) {
        this.currentPoint = ponit;
    }

    public MyPoint getCurrentPoint() {
        return currentPoint;
    }

    public void setCurrentPoint(MyPoint currentPoint) {
        this.currentPoint = currentPoint;
        invalidate();
    }
}

           

然後在activity中這樣使用,即可實作

button.setOnClickListener(new View.OnClickListener() {
          
            @Override
            public void onClick(View v) {
                MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始點為圓心(70,70)
                MyPoint endPoint = new MyPoint(200, 250);// 結束點為(200, 250)
                ObjectAnimator oa = ObjectAnimator.ofObject(criView, "currentPoint", new PointEvaluator(), startPoint, endPoint);
                oa.setDuration(3000);
                oa.start();
            }
        });
           

3.AnimatorSet

  • play(Animator anim) : 播放目前動畫
  • after(Animator anim): 将現有動畫插入到傳入的動畫之後執行。
  • after(long delay): 将現有動畫延遲指定毫秒後執行。
  • before(Animator anim): 将現有動畫插入到傳入的動畫之前執行
  • with(Animator anim): 将現有動畫和傳入的動畫同時執行
  • playTogether(): 動畫一起執行
  • playSequentially():動畫依次執行
    Android中的屬性動畫
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.play(oa1).with(oa2).after(oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });
           
Android中的屬性動畫
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playSequentially(oa1,oa2,oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });
           
Android中的屬性動畫
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(oa1,oa2,oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });
           

繼續閱讀