小菜鳥打怪系列之屬性動畫繪製原理

本文主要解決如下問題:java

  • 動畫的總體框架?
  • view是如何動起來的?

好了,接下來,開始打怪~數組

1、動畫的總體框架

首先,咱們平時寫屬性動畫是這麼寫的:框架

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v,"scaleX",0,1);
        objectAnimator.setEvaluator(new FloatEvaluator());
        objectAnimator.setInterpolator(new LinearInterpolator());
        objectAnimator.setDuration(1000);
        objectAnimator.start();
複製代碼

so,咱們先從ofFloat開始ide

//一、入口
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }
    
    //二、看new ObjectAnimator作了什麼?
        private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }
    
    //三、setTarget 進去look look
        @Override
    public void setTarget(@Nullable Object target) {
        .....
        //就弱引用,沒啥
            mTarget = target == null ? null : new WeakReference<Object>(target);
          ....
        }
    }
    //四、setPropertyName 瞄瞄
    
        public void setPropertyName(@NonNull String propertyName) {
       ......
       //依舊只是保存值
        mPropertyName = propertyName;
       ......
    }
    
    // 五、看看anim.setFloatValues(values) 幹了啥
       @Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            if (mProperty != null) {
            //注意注意,重點人物之一的PropertyValuesHolder出現了!!!
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }
//六、跟蹤 PropertyValuesHolder.ofFloat 方法,最終會調用setFloatValues這個方法
    public void setFloatValues(float... values) {
        mValueType = float.class;
        //注意注意,重點人物 KeyframeSet 也出現了!!!
        mKeyframes = KeyframeSet.ofFloat(values);
    }
    
  // 七、跟蹤KeyframeSet.ofFloat方法 
        public static KeyframeSet ofFloat(float... values) {
       .....
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            .....
        } else {
        //能夠看到,這裏根據values的長度,構造了keyframes數組,
        //而後分別經過Keyframe的ofFloat方法,去構造keyframe對象
        //Keyframe就是關鍵幀,它裏面保存了類型、數值以及估值器
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        ......
        return new FloatKeyframeSet(keyframes);
    }
    
// 九、仍是大家看一個Keyframes源碼吧,其實就是一個接口

public interface Keyframes extends Cloneable {

    void setEvaluator(TypeEvaluator evaluator);
    Class getType();
    Object getValue(float fraction);
    List<Keyframe> getKeyframes();
    Keyframes clone() .... } 複製代碼

第一步咱們就跟完了,咱們看到,在初始化的整個過程當中,涉及到了如下幾個對象:oop

而這幾個對象就構成了咱們屬性動畫的框架。post

接下來,咱們要來看一下咱們的view是怎麼動起來的~動畫

2、view是怎麼動起來的

//一、從 objectAnimator.start()進入
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
       .....
        super.start();
    }
 
 //二、點擊super.start()一路跟到ValueAnimator的start(playBackwrds)方法
 private void start(boolean playBackwards) {
      .....
      //跟蹤這個方法
        addAnimationCallback(0);
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        ......
        //這個方法其實就是去初始化Animation,這裏就不跟蹤了
        //小夥伴能夠進行去閱讀
            startAnimation();
            if (mSeekFraction == -1) {
                .....
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }
    
//三、跟蹤 addAnimationCallback(0)方法 ,最後能夠來到AnimationHandler中的addAnimationFrameCallback方法
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    //這裏發送了一個mFrameCallback,跟一下mFrameCallback
       if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
       //在這裏添加了一個callback,而且這個callback是ValueAnimator!!記住,很重要!!
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }
....
    } 
  //四、mFrameCallback
  private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };
   //五、看Choreographer.FrameCallback的定義
    /** * Implement this interface to receive a callback when a new display frame is * being rendered. The callback is invoked on the {@link Looper} thread to * which the {@link Choreographer} is attached. */
    public interface FrameCallback {
        /** * Called when a new display frame is being rendered. * <p> * 也就是說,每次從新開始渲染屏幕上的幀時,這個方法將會被調用。 */
        public void doFrame(long frameTimeNanos);
  }
  //六、那這個doFrame方法在哪裏被調用呢?用doFrame做爲關鍵字在Choreographer查找,最後發現,在這裏進行了調用
  private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

//看到熟悉的Vysnc沒,從字眼咱們也大概能夠推斷,
//每次Vysnc信號來時,都會回調這個方法,最終會執行run()方法裏面的代碼
//最後,回調回去咱們的Choreographer.FrameCallback的doFrame方法
//這個回調的頻率大概是16ms一次
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
           ....
           Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }
    
//七、這一段追完了,回去咱們的 mFrameCallback 
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos){
        //跟進這個方法
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };
//八、 doAnimationFrame
    private void doAnimationFrame(long frameTime) {
        long currentTime = SystemClock.uptimeMillis();
        final int size = mAnimationCallbacks.size();
        for (int i = 0; i < size; i++) {
            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
            if (callback == null) {
                continue;
            }
            if (isCallbackDue(callback, currentTime)) {
            //還記得前面add了個callback嗎?那個callback是ValueAnimator,因此,如今咱們要去看一下ValueAnimator的doAnimationFrame方法了
                callback.doAnimationFrame(frameTime);
                .....
            }
        }
        ....
    }
//九、 ValueAnimator的doAnimationFrame ,還有幾步就完了,堅持一下
    public final boolean doAnimationFrame(long frameTime) {
     ....
     //重點在這個方法
        boolean finished = animateBasedOnTime(currentTime);
    ....
        return finished;
    }
    
//十、animateBasedOnTime
 boolean animateBasedOnTime(long currentTime) {
          ....
          //跟進這個方法,這裏要注意,若是直接點擊去,會去到ValueAnimator裏面的animateValue,
          //可是實際上,ObjectAnimator覆蓋了這個方法,因此咱們要去看ObjectAnimator的animateValue方法
            animateValue(currentIterationFraction);
         ....
    }
//十一、ObjectAnimator的animateValue方法
   @Override
    void animateValue(float fraction) {
      ....
      //super.animateValue能夠本身去跟,其實就是利用interpolator和evaluator計算出當前動畫對應的數值
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
        //看這個setAnimatedValue方法
            mValues[i].setAnimatedValue(target);
        }
    }
、、12、PropertyValuesHolder的 setAnimatedValue void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                //看到沒,其實就是經過反射動態調用setXX方法
                //經過set方法去動態改變View的屬性,從而實現動畫效果
                mSetter.invoke(target, mTmpValueArray);
            } ...
        }
    }
複製代碼

呼,累死了~~ui

到這裏,讓view動起來的整個流程就結束了。總結一下:this

  • 監聽Vsync信號,每次信號一來就回調ValueAnimatordoAnimationFrame方法
  • 經過InterpolatorEvaluator計算出當前動畫的百分比和具體數值
  • 調用PropertyValuesHoldersetAnimatedValue方法,去反射調用setXX,從而實現動態改變view的屬性

簡化後的流程就是:lua

本文結束!


感謝觀看,有不對的地方,歡迎你們指出~~

相關文章
相關標籤/搜索