Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 屬性動畫進階總結篇(篇3)

Android 屬性動畫進階總結篇(篇3)

編輯:關於Android編程

1.總結之前學習的關於屬性動畫的筆記 和 學習了郭霖大神的 屬性動畫高級用法相關的博客之後的記錄

2.學習loading…

2.1補間動畫 PK 屬性動畫

在篇1已經列舉了大致的用法; 補間動畫只能對View對象進行動畫操作,而屬性動畫則可以對任意對象進行動畫操作; 補間動畫只是改變了View的顯示效果,而不會去改變View的屬性;

2.2開始學習

1.首先認識下屬性動畫中非常核心的一個類,就是FloatEvaluator類,它主要告訴動畫系統如何從初始值過渡到結束值,它是系統默認的TypeEvaluator的實現類.

public class FloatEvaluator implements TypeEvaluator {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}

2.具體的代碼步驟詳解可以查看上面的博客鏈接,這裡就直接代碼走起


    /**
     * Created by wyk on 2016/9/25.
     * 描述:告訴動畫系統從初始值過渡到結束值,屬性動畫在系統中有個默認的TypeEvaluator,它就是
     * FloatEvaluator,繼承自TypeEvaluator,屬性動畫的高級用法中最有技術含量的也就是如何編寫出
     * 一個合適的TypeEvaluator
     *
     * 作用:根據fraction的變換得到圓球的中點,裝載在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());

            return new Point(x,y);
        }
    }

具體介紹 在代碼中的注釋中已詳細說明


    /**
     * Created by wyk on 2016/9/25.
     * 描述:記錄圓球的中心在x軸y軸上的值
     * 作用:1.供PointEvaluator去記載當前位置
     *      2.在CircleView類中可以根據Point的xy繪制圓球
     */
    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;
        }
    }

自定義View,實現圓球的動畫效果


    /**
     * Created by wyk on 2016/9/25.
     * 描述:自定義View,實現圓球的動畫效果
     * 作用:1.學習屬性動畫的核心類;
     *      2.學習ObjectAnimator內部的工作機制,主要通過尋找對象特定屬性的get和set方法,
     *        然後通過方法不斷地對特定屬性值進行改變,從而實現動畫效果
     *      3.溫故而知新,重新學習下屬性動畫的api
     *      4.熟悉自定義控件中自定義屬性的聲明以及應用
     *
     */
    public class CircleView extends View {

        private static final float RADIUS = 50F;
        private Paint mPaint;
        private Point currenPoint;
        private int color;
        private float radius;
        private float delay;
        private ValueAnimator vAnimator;
        private ObjectAnimator colorAnimator;

        public CircleView(Context context) {
            this(context,null);
        }
        public CircleView(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
        public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);

            initView(context,attrs);
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

            if(color != 0){
                mPaint.setColor(color);
                return;
            }
            mPaint.setColor(Color.BLUE);
        }

        /**讀取自定義屬性,使用者可以直接在布局文件中使用該View和其定義的屬性*/
        private void initView(Context context, AttributeSet attrs) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable
                    .circle_view_style);

            color = typedArray.getColor(R.styleable.circle_view_style_circle_color,getResources()
                    .getColor(R.color.colorAccent));
            radius = typedArray.getFloat(R.styleable.circle_view_style_circle_radius, 50);
            delay = typedArray.getFloat(R.styleable.circle_view_style_circle_start_delay, 500);

            typedArray.recycle();                           //note
        }


        @Override
        protected void onDraw(Canvas canvas) {
           if(currenPoint == null){
               if(radius == 0){
                   currenPoint = new Point(RADIUS,RADIUS);
               }else{
                   currenPoint = new Point(radius,radius);
               }
                drawCircle(canvas);
                startAnimator();                            //開啟動畫
            }else{
               drawCircle(canvas);
           }
        }

        private void drawCircle(Canvas canvas) {            //除了第一次View自身繪制會進來該方法,
                                                             // 之後都是由於ValueAnimator的監聽改變而調用View的onDraw方法,從而調用該方法
            float x = currenPoint.getX();
            float y = currenPoint.getY();
            canvas.drawCircle(x,y,radius,mPaint);
        }

        private void startAnimator() {
            Point startPoint = new Point(RADIUS,RADIUS);
            Point endPoint = new Point(getWidth() - RADIUS , getHeight() - RADIUS);
            vAnimator = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
            vAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    currenPoint = (Point)animation.getAnimatedValue();
                    invalidate();                                               //會調用onDraw方法,不斷重繪
                }
            });

            colorAnimator = ObjectAnimator.ofObject(this, "circleColor", new
                    ColorEvaluator(), "#0000ff", "#ff0000");

            vAnimator.setRepeatCount(ValueAnimator.INFINITE);
            vAnimator.setRepeatMode(ValueAnimator.REVERSE);

            AnimatorSet anSet = new AnimatorSet();
            anSet.play(vAnimator).with(colorAnimator);
            anSet.setDuration(2000);

            anSet.start();

           /* vAnimator.setDuration(2000);
            vAnimator.setStartDelay((long) delay);
            vAnimator.setRepeatCount(100);
            vAnimator.setRepeatMode(ValueAnimator.REVERSE);
            vAnimator.setInterpolator(new OvershootInterpolator());
            vAnimator.start();*/
        }


        public String circleColor;

        /**ObjectAnimator動畫的核心*/
        public String getCircleColor() {
            return circleColor;
        }
        public void setCircleColor(String circleColor) {
            this.circleColor = circleColor;
            mPaint.setColor(Color.parseColor(circleColor));
            invalidate();
        }
    }

    /**
     * Created by wyk on 2016/9/25.
     * 描述:告訴動畫系統從初始值過渡到結束值,屬性動畫在系統中有個默認的TypeEvaluator,
     * 它就是FloatEvaluator,繼承自TypeEvaluator
     * 屬性動畫的高級用法中最有技術含量的也就是如何編寫出一個合適的TypeEvaluator
     *
     * 作用:該類主要進行計算當前的顏色值
     */

    public class ColorEvaluator implements TypeEvaluator {

        private int mCurrentRed = -1;
        private int mCurrentGreen = -1;
        private int mCurrentBlue = -1;

        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            String startColor = (String)startValue;
            String endColor = (String)endValue;

            int startRed = Integer.parseInt(startColor.substring(1,3),16);        //16進制轉換成10進制
            int startGreen = Integer.parseInt(startColor.substring(3,5),16);
            int startBlue = Integer.parseInt(startColor.substring(5,7),16);
            int endRed = Integer.parseInt(endColor.substring(1,3),16);
            int endGreen = Integer.parseInt(endColor.substring(3,5),16);
            int endBlue = Integer.parseInt(endColor.substring(5,7),16);

            //初始化開始顏色
            if(mCurrentRed == -1){
                mCurrentRed = startRed;
            }
            if(mCurrentGreen == -1){
                mCurrentGreen = startGreen;
            }
            if(mCurrentBlue == -1){
                mCurrentBlue = startBlue;
            }

            //計算顏色的差值
            int redDiff = Math.abs(startRed - endRed);
            int greenDiff = Math.abs(startGreen - endGreen);
            int blueDiff = Math.abs(startBlue - endBlue);
            int colorDiff = redDiff + greenDiff + blueDiff ;

            if(mCurrentRed != endRed){
                mCurrentRed = getCurrentColor(startRed,endRed,colorDiff,0,fraction);
            }else if(mCurrentGreen != endGreen){
                mCurrentGreen = getCurrentColor(startGreen,endGreen,colorDiff,redDiff,fraction);
            }else if(mCurrentBlue != endBlue){
                mCurrentBlue = getCurrentColor(startBlue,endBlue,colorDiff,redDiff + greenDiff,
                                                fraction);
            }
            //將計算出的顏色組裝之後進行返回
            String currentColor = "#" + getHexString(mCurrentRed) + getHexString(mCurrentGreen) +
                    getHexString(mCurrentBlue);

            return currentColor;
        }
        //根據fraction來計算當前的顏色值
        private int getCurrentColor(int startColor, int endColor, int colorDiff, int offset, float
                fraction) {
            int currentColor;
            if(startColor > endColor){
                currentColor = (int)(startColor - (fraction * colorDiff - offset));
                if(currentColor < endColor){            //如果不進行這個判斷的話.currentColor可能會小於endColor,// 更是小於startColorl;
                    currentColor = endColor;
                }
            }else{
                currentColor = (int) (startColor + (fraction * colorDiff - offset));
                if(currentColor > endColor){
                    currentColor = endColor;
                }
            }
        //上面主要是保證   currentColor主要在  [startColor,endColor] 這個區間內
         return currentColor;
        }

        //將10進制轉換成16進制
        private String getHexString(int value){
            String hexString = Integer.toHexString(value);
            if(hexString.length() == 1){
                hexString = "0" + hexString;                    //轉換成16進制的時候,可能只有0-9,abcdef這些,即只有一位,故前邊補0
            }
            return hexString;
        }
    }
3.最後貼上MainActivity的代碼和Layout布局

    /**
     * Created by wyk on 2016/9/25.
     * 作用:展示CircleView的動畫效果
     */
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
           // setContentView(R.layout.activity_main);                //學習順序01
            setContentView(R.layout.activity_main01);                //學習順序02

        }
    }


    /**
    *activity_main.xml布局
    */
    
    
        
        
            

        
        
            

        
        
            

        
    


    /**
    *activity_main01.xml布局
    */
    
    

            

    

4.對了,還有一件重要的事情還沒做,自定義屬性部分的code,罪過罪過!

    /**
     *在res/values目錄下創建attrs.xml
     *具體聲明 看下面
     */
    
    
        
            
            
            
        
    


3.在上面的CirlceView類的startAnimator 方法中加了如下兩句:

vAnimator.setRepeatCount(ValueAnimator.INFINITE);
vAnimator.setRepeatMode(ValueAnimator.REVERSE);

主要作用是讓動畫可以重復執行,那麼就出現這麼一個問題:

圓球走到右下角後,回到左上角,再依次執行的過程中,顏色並無改變;

看到大神的博客評論區有如下評論
這裡寫圖片描述

針對上面評論所說的解決方法是可行的,前提是基於大神的博客,效果是 “圓球從左上角移動到右下角,這一過程循環執行”,由於本份代碼設置了動畫的模式為 “ValueAnimator.REVERSE”,所以效果是 “圓球從左上角移動到右下角,接著從右下角移動回左上角,這一過程循環執行”,所以設置 “ValueAnimator.INFINITE” 並不起作用.

解決方法(直接上代碼,代碼裡有詳細的注釋)


    /**
     * CircleView類
     */
    private void startAnimator() {
        Point startPoint = new Point(RADIUS,RADIUS);
        Point endPoint = new Point(getWidth() - RADIUS , getHeight() - RADIUS);
        vAnimator = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        vAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currenPoint = (Point)animation.getAnimatedValue();
                invalidate();                                               //會調用onDraw方法,不斷重繪
            }
        });
        colorAnimator = ObjectAnimator.ofObject(this, "circleColor", new
                ColorEvaluator(), "#0000ff", "#ff0000");

        vAnimator.setRepeatCount(ValueAnimator.INFINITE);
        vAnimator.setRepeatMode(ValueAnimator.REVERSE);

        anSet = new AnimatorSet();
        anSet.play(vAnimator).with(colorAnimator);
        anSet.setDuration(5000);
        anSet.start();

        vAnimator.addListener(new AnimatorListenerAdapter() {
            //AnimatorListenerAdapter是監聽執行操作的動畫適配器類,實現於Animator.AnimatorListener,
            // 當然我們也可以在這裡創建AnimatorListener類,但是由於實現該接口必須重寫裡面的6個方法,
            // 而AnimatorListenerAdapter實現於Animator.AnimatorListener,通過重寫裡面的6個方法,方法體為空,
            //我們可以更簡便的挑選所需要的接口來使用;

            @Override
            public void onAnimationRepeat(Animator animation) { //動畫重復執行時,會被調用,例如小球回到左上角/右下角都會被調用

                //在達到底部的時候,如果不想圓球的顏色由紅色直接變成藍色,這裡可以設置Tag進行標識
                //當tag為0 的時候,顏色由#ff0000" ---過渡--> "#0000ff;
                // 當tag為1 的時候,顏色由#0000ff" ---過渡--> "#ff0000;關於改變顏色突變的效果的代碼就不寫了

                colorAnimator = ObjectAnimator.ofObject(CircleView.this, "circleColor", new
                        ColorEvaluator(), "#0000ff", "#ff0000");
                colorAnimator.setDuration(5000);
                colorAnimator.start();

            }
        });
    }

4.總結:

1.屬性動畫不同於補間動畫,補間動畫主要改變的是顯示效果,其屬性根本就木有被改變;例如舉個簡單的小例子,我們對一個設置了點擊後show Toast的按鈕執行補間動畫,從位置A移動到位置B後,點擊位置B的按鈕,並無任何反應,再點擊位置A,A位置可是沒有按鈕額,可是就有show Toast的效果;

2.屬性動畫的擴展性很強大,除了對View進行動畫操作,還可以對任意對象進行動畫操作,其核心是TypeEvaluator,主要告訴動畫系統如何從初始值過渡到結束值,在其evaluate method中進行計算得到當前值 然後返回;

3.ObjectAnimator繼承自ValueAnimator,其工作機制是通過特定屬性的get/set方法對屬性值進行改變,從而實現動畫效果;

敲了這麼多行代碼,怎能少了演示效果,這裡由於手機錄屏軟件由於格式導致錄制不成功,直接借鑒郭霖 大神的效果圖,如有侵權,請告知刪除:

這裡寫圖片描述

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved