天天看點

Android屬性動畫用法(中)

           昨天,我們通過Java代碼的形式實作了一些簡單的屬性動畫效果,今天,讓我們通過xml檔案來實作一下同樣的效果,之後,我們再用屬性動畫實作小球的運動。雖然xml寫起來比較麻煩,不像昨天代碼中編寫的隻需要幾行代碼就搞定,但是xml形式好在易于重用,不需要在每個界面都實作一下同樣的效果,下面讓我們看看如何實作吧:

使用XML來編寫動畫,首先要在res目錄下面建立一個animator檔案夾,所有屬性動畫的XML檔案都應該存放在這個檔案夾當中。然後在XML檔案中我們一共可以使用如下三種标簽:

  • <animator>  對應代碼中的ValueAnimator
  • <objectAnimator>  對應代碼中的ObjectAnimator
  • <set>  對應代碼中的AnimatorSet

實作昨天效果代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:duration="2000"
        android:propertyName="translationX"
        android:valueFrom="-600"
        android:valueTo="0"
        android:valueType="floatType" >
    </objectAnimator>
    <objectAnimator
        android:duration="2000"
        android:propertyName="alpha"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType" >
    </objectAnimator>


</set>      

set标簽中的ordering屬性為動畫制定執行順序,然後我們在activity啟動這個動畫:

  1. Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_test);  
  2. animator.setTarget(textview);  
  3. animator.start();

效果就不示範了,同昨天一樣的。

下面,我們來看一下屬性動畫更加進階一點的用法,之是以稱之為進階,主要展現在屬性動畫中TypeEvaluator接口實作中,TypeEvaluator是用來幹什麼的呢?

  • 屬性動畫進階- TypeEvaluator的使用

簡單來說,就是告訴動畫系統如何從初始值過度到結束值。因為之前的動畫過渡過程是系統負責的,我們并沒有做出相應的幹涉和處理,如果你想實作更加複雜和炫酷的動畫效果,這個接口相信你會經常用到。

上一篇文章中學到的ValueAnimator.ofFloat()方法就是實作了初始值與結束值之間的平滑過度,那麼這個平滑過度是怎麼做到的呢?其實就是系統内置了一個FloatEvaluator,它通過計算告知動畫系統如何從初始值過度到結束值,我們來看一下FloatEvaluator的代碼實作:

[java]  view plain  copy  

Android屬性動畫用法(中)
Android屬性動畫用法(中)
  1. public class FloatEvaluator implements TypeEvaluator {  
  2.     public Object evaluate(float fraction, Object startValue, Object endValue) {  
  3.         float startFloat = ((Number) startValue).floatValue();  
  4.         return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
  5.     }  
  6. }  

可以看到,FloatEvaluator實作了TypeEvaluator接口,然後重寫evaluate()方法。evaluate()方法當中傳入了三個參數,第一個參數fraction非常重要,這個參數用于表示動畫的完成度的,我們應該根據它來計算目前動畫的值應該是多少,第二第三個參數分别表示動畫的初始值和結束值。那麼上述代碼的邏輯就比較清晰了,用結束值減去初始值,算出它們之間的內插補點,然後乘以fraction這個系數,再加上初始值,那麼就得到目前動畫的值了。好的,那FloatEvaluator是系統内置好的功能,并不需要我們自己去編寫,但介紹它的實作方法是要為我們後面的功能鋪路的。

上面的方法是對浮點值進行操作的,如果我們想要實作複雜效果,肯定不僅僅局限于這幾種常用類型,對象是肯定需要用到的,那麼到底該如何使用呢?我們通過一個小例子來看一下實作思路:

  • 如何實作自定義view小球的運動?

在自定義view中,我們經常用到小球這個簡單的view實作一些效果,大家都知道,我們一般使用坐标來實作它位置的不斷重繪,進而實作動畫效果,是以,讓它運動的方法就是隻要不斷改變它的中心點坐标就可以了。那麼,我們要通過屬性動畫實作小球運動,改變的不是所謂int或者float型的初始值和結束值,而是點坐标即Point對象。既然抓住了突破點,我們就可以下手去做了。

1.首先,我們定義一個Point類,表示小球中心點的坐标:

/**
 * Created by MuFeng on 2017/4/17.
 * fc:坐标點
 */

public class Point {
    private float x;
    private float y;

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }
}
      

代碼很簡單,讓我們直接思考下一步該如何入手呢?剛剛上面說過了,TypeEvaluator這個接口用來處理起始值與結束值之間過渡過程的處理,因為我們這裡重新定義一個自己的類型作為初始值和結束值,是以,理所當然地要實作它的過渡過程的邏輯,那麼下一步當然是寫一個實作類了。

2.PointEvaluator類實作TypeEvaluator接口:

/**
 * Created by MuFeng on 2017/4/17.
 * fc:制定自己的動畫過渡過程,這裡實作的是初始坐标到終點坐标的過渡
 */

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首先要重寫它的方法:evaluate()。點的變化取決于橫坐标和縱坐标,是以我們隻需要将x與y過渡的過程實作一下就好了,這裡我們先要定義兩個點用于方法傳入的起始值與結束值,然後對他們的橫縱坐标進行處理,依舊采用的是起始值+fraction*(終點x/y-起點x/y)來得到的,最後建立一個Point傳入計算得到的x與y即可。這樣,我們的起點到終點的過渡實作就完成了,那下面我們該畫出小球并實作其動畫效果了,然而并不是!在此之前,我們需要先看一下這個Point以及過渡邏輯能否有效,我們先對一個點進行動畫操作看看;

3.對Point進行動畫操作,檢視過渡過程的資料:

根據前面的學習,我們知道ValueAnimator是對值的操作,是以,這裡我們就用它看一下點運動的軌迹:

/**
 * point 動畫
 */
private void pointAnimation() {

    Point start=new Point(0,0);
    Point end=new Point(200,200);
    ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);
    valueAnimator.setDuration(3000);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //擷取目前的point
            Point currentPoint= (Point) animation.getAnimatedValue();
            float x=currentPoint.getX();//擷取目前點的x
            float y=currentPoint.getY();//擷取目前點的y
            Log.d("目前點坐标為:","("+x+","+y+")");

        }
    });
    valueAnimator.start();
}      

這裡我們調用的是ofObject()方法來對對象進行動畫操作,第一參數是我們自定義的過度規則,第二第三個則是起點與終點了,我們通過監聽器實時監聽過渡過程point的變化,讓我們檢視一下LOG資訊:

Android屬性動畫用法(中)

發現point的确是在不斷的變化,最終到達終點的,說明我們前面的工作有效的,下面讓我開始實作小球的動畫吧。

3.小球運動動畫實作:

由上面的分析可知,想要實作小球的運動,隻要實作它中心點的運動即可,上面我們已經完成了該項工作,說明我們已經離成功還差一小步了,我們先來自定義一個BallView繼承自view類,實作裡面的構造方法、ondraw()方法以及小球運動的方法即可:

package test.myanimatortest;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by MuFeng on 2017/4/17.
 * fc:自定義view,實作小球的運動
 */

public class BallView extends View {
    Point currentPoint;//目前point
    Paint paint;//畫筆
    public static final float R=50f;
    public BallView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint=new Paint(Paint.ANTI_ALIAS_FLAG);//抗鋸齒
        paint.setColor(Color.RED);
        


    }
    /**
     * 在畫布上畫一個球
     */
    private void drawBall(Canvas canvas){
        float x=currentPoint.getX();
        float y=currentPoint.getY();
        canvas.drawCircle(x,y,R,paint);
    }
    /**
     * ondraw()重寫
     */
    @Override
    protected void onDraw(Canvas canvas) {
        if (currentPoint==null){
            currentPoint=new Point(100,100);
            drawBall(canvas);//畫球
            //startSport();//開始運動
        }else {
            drawBall(canvas);
        }
    }

    /**
     * 小球的運動動畫
     */
    public void startSport() {
        //設定起點終點
        Point startPoint=new Point(100,100);
        Point endPoint=new Point(getWidth()- R,getHeight()-R);
        ValueAnimator ballAnim=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        ballAnim.setDuration(3000);
        //監聽動畫過程
        ballAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPoint= (Point) animation.getAnimatedValue();//每次變化,都會重新設定currentPoint的位置
                invalidate();//每次位置改變都重繪一下小球位置
            }
        });
        ballAnim.start();

    }
}
      

我們設定好畫筆以及目前點位置資訊後,在ondraw()方法中畫出一個小球,判斷一下,如果目前的point是空的,則重新建立一個,再調用drawBall()方法畫出半徑為50.0的小球,小球動畫是通過startSport()這個方法實作的,我們指定好起點終點後,通過ValueAnimator調用ofObject()方法傳入PointEvaluator對象和起始點、結束點,接着通過監聽器監聽它的動畫過程,方法中,每當point位置發生改變,都會重新給小球位置指派,再調用invalidate()方法不斷重繪,進而實作小球的運動效果。

4.最後,在布局檔案中加入自定義view,activity中按鈕點選實作小球運動:

布局檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_sport_animation"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="test.myanimatortest.SportAnimationActivity">

    <Button
        android:id="@+id/startSport"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="開始"
        android:layout_margin="16dp"/>
    <test.myanimatortest.BallView
        android:id="@+id/ball"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
      

然後在activity中擷取view并且實作點選按鈕,小球運動的功能:

public class SportAnimationActivity extends AppCompatActivity implements View.OnClickListener {
    private Button start;
    private BallView ballView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sport_animation);
        start= (Button) findViewById(R.id.startSport);
        ballView= (BallView) findViewById(R.id.ball);
        start.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.startSport:
                //pointAnimation();
                ballView.startSport();//通過點選“開始”按鈕,開始小球的運動

                break;
        }

    }

    /**
     * point 動畫
     */
    private void pointAnimation() {

        Point start=new Point(0,0);
        Point end=new Point(200,200);
        ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //擷取目前的point
                Point currentPoint= (Point) animation.getAnimatedValue();
                float x=currentPoint.getX();//擷取目前點的x
                float y=currentPoint.getY();//擷取目前點的y
                Log.d("目前點坐标為:","("+x+","+y+")");

            }
        });
        valueAnimator.start();
    }

}
      

代碼有注釋,了解很簡單,這裡就不多說了,看下運作後的效果吧:

Android屬性動畫用法(中)

有點卡,總之效果是實作了,今天内容就到這吧,後面會找個屬性動畫的實戰訓練一下自己,持續更新中。

補充:本文僅作學習記錄和供大家參考,如果有不足和改進之處歡迎大家指出,謝謝。

參考:

http://blog.csdn.net/guolin_blog/article/details/43816093,特别緻謝。

http://blog.csdn.net/chenzheng8975/article/details/53710492