源碼閱讀_屬性動畫執行過程

源碼閱讀_屬性動畫執行過程

在Android開發中有很多地方使用到了屬性動畫,閱讀源碼是熟悉API以及相關代碼最直接的方式。

在平時開發中使用屬性動畫最多的就是這類的代碼

       val animator = ObjectAnimator.ofFloat(mView, "alpha", 0f, 100f, 50f)
       animator.duration = 1000
       animator.startDelay = 300
       animator.start()

封裝數據

代碼中第一句ObjectAnimator.ofFloat(…),直接打開源碼(ObjectAnimator.java)

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
       ObjectAnimator anim = new ObjectAnimator(target, propertyName);
       anim.setFloatValues(values);
       return anim;
   }

在ofFloat方法中第一行代碼是創建一個ObjectAnimator對象,構造函數中傳入執行動畫的View,以及屬性名。

private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

打開構造函數的代碼,可以看到函數中又調用兩個方法,看意思感覺挺明顯一個是設置View,一個是設置屬性名。

還是看一下setTarget方法和setPropertyName方法具體執行了什麼邏輯

setTarget方法中,

  1. 先獲取mTarget,判斷獲取的mTarget和目標View是否是同一個控件,很明顯之前沒有設置過mTarget,所以if (oldTarget != target)爲true;
  2. 判斷動畫狀態,如果是開始執行動畫了就取消;
  3. 將目標View保存到全局變量mTarget中。
private WeakReference<Object> mTarget;

public Object getTarget() {
        return mTarget == null ? null : mTarget.get();
}

@Override
   public void setTarget(@Nullable Object target) {
       final Object oldTarget = getTarget();
       if (oldTarget != target) {
           if (isStarted()) {
               cancel();
           }
           mTarget = target == null ? null : new WeakReference<Object>(target);
           // New target should cause re-initialization prior to starting
           mInitialized = false;
       }
   }

setPropertyName方法中

  1. 判斷mValues是否爲空;(第一次一定爲空, 如果不爲空,則用當前值替換數組中的第一個值,以及替換mValuesMap
  2. 保存屬性名至全局變量mPropertyName
  3. 設置初始化標識false
    //用於保存屬性名的數組
    PropertyValuesHolder[] mValues;
    //用於保存屬性名的HashMap
    HashMap<String, PropertyValuesHolder> mValuesMap;

   public void setPropertyName(@NonNull String propertyName) {
        // mValues could be null if this is being constructed piecemeal. Just record the
        // propertyName to be used later when setValues() is called if so.
        if (mValues != null) {
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
            mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

這樣構造函數執行的內容都搞清楚了,繼續看下面設置的代碼

        animator.duration = 1000
        animator.startDelay = 300

這部分源碼就很簡單了,就是保存屬性

// ObjectAnimator類中
   @Override
   @NonNull
   public ObjectAnimator setDuration(long duration) {
       super.setDuration(duration);
       return this;
   }

//父類ValueAnimator中
   @Override
   public ValueAnimator setDuration(long duration) {
       if (duration < 0) {
           throw new IllegalArgumentException("Animators cannot have negative duration: " +
                   duration);
       }
       mDuration = duration;
       return this;
   }

註冊垂直同步信號

繼續下一行代碼animator.start()

這個方法裏面主要操作一個是取消監聽,一個是執行父類的start()方法。


    @Override
    public void start() {
        //AnimationHandler 並不是Android中的Handler,這裏可以理解爲一個單例工具類
        //取消掉animator之前設置的回調
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        if (DBG) {
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();
    }

start()方法中首先後去獲取了一個AnimationHandler對象的單例,再調用這個單例的autoCancelBasedOn()方法。
其實AnimationHandler對象並不是Android中的Handler,這裏可以理解爲一個工具類。
其中的autoCancelBasedOn()方法中執行的內容大致爲遍歷取消動畫下一幀回調監聽。(至於哪裏註冊、使用到這個監聽,下面會講到)


public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();

public static AnimationHandler getInstance() {
    if (sAnimatorHandler.get() == null) {
        sAnimatorHandler.set(new AnimationHandler());
    }
    return sAnimatorHandler.get();
}   

void autoCancelBasedOn(ObjectAnimator objectAnimator) {
       for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
           AnimationFrameCallback cb = mAnimationCallbacks.get(i);
           if (cb == null) {
               continue;
           }
           if (objectAnimator.shouldAutoCancel(cb)) {
               ((Animator) mAnimationCallbacks.get(i)).cancel();
           }
       }
   }

取消完監聽之後調用了父類的start()方法,父類的start()方法中又調用了重載的start(false)方法。

這個重載的start(false)方法就比較重要了,一開始它檢查了是否在UI線程執行,又設置動畫是否反向播放(從來沒有用過反向播放),反向播放的操作邏輯,以及一些狀態設置等等。

接下來執行了它比較重要的三個方法addAnimationCallback(0)startAnimation();setCurrentPlayTime(0)

@Override
   public void start() {
       //重載start的方法  參數是是否反向播放
       start(false);
   }

   private void start(boolean playBackwards) {
       if (Looper.myLooper() == null) {
           throw new AndroidRuntimeException("Animators may only be run on Looper threads");
       }
       // 動畫是否反向播放
       mReversing = playBackwards;
       // mSuppressSelfPulseRequested,這個值通過方法startWithoutPulsing設置,就把他默認當作false即可
       mSelfPulse = !mSuppressSelfPulseRequested;
       // Special case: reversing from seek-to-0 should act as if not seeked at all.
       //反向播放的邏輯
       if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
           if (mRepeatCount == INFINITE) {
               // Calculate the fraction of the current iteration.
               float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
               mSeekFraction = 1 - fraction;
           } else {
               mSeekFraction = 1 + mRepeatCount - mSeekFraction;
           }
       }
       //        狀態設置
       mStarted = true;
       mPaused = false;
       mRunning = false;
       mAnimationEndRequested = false;
       // Resets mLastFrameTime when start() is called, so that if the animation was running,
       // calling start() would put the animation in the
       // started-but-not-yet-reached-the-first-frame phase.
       mLastFrameTime = -1;
       mFirstFrameTime = -1;
       mStartTime = -1;
       addAnimationCallback(0);

       if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
           // If there's no start delay, init the animation and notify start listeners right away
           // to be consistent with the previous behavior. Otherwise, postpone this until the first
           // frame after the start delay.
           startAnimation();
           if (mSeekFraction == -1) {
               // No seek, start at play time 0. Note that the reason we are not using fraction 0
               // is because for animations with 0 duration, we want to be consistent with pre-N
               // behavior: skip to the final value immediately.
               setCurrentPlayTime(0);
           } else {
               setCurrentFraction(mSeekFraction);
           }
       }
   }

addAnimationCallback(0)方法中,主要執行的邏輯就是前面提到過的AnimationHandler中註冊AnimationFrameCallback監聽。

因爲前面移除了所有監聽所以代碼會執行getProvider().postFrameCallback(mFrameCallback)

// ValueAnimator 類中
private void addAnimationCallback(long delay) {
      if (!mSelfPulse) {
          return;
      }
      getAnimationHandler().addAnimationFrameCallback(this, delay);
  }

  /**
     * AnimationHandler 的 addAnimationFrameCallback方法代碼
     * Register to get a callback on the next frame after the delay.
     */
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

先看getProvider().postFrameCallback(mFrameCallback)這行代碼的參數mFrameCallback,再去了解getProvider方法。

這是一個回調接口,回調方法doFrame中只要mAnimationCallbacks大於0就會調用getProvider().postFrameCallback(this),這就有點動畫反覆繪製的意思了。(doAnimationFrame方法下面再講)

    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };

繼續看getProvider方法。方法中創建了一個對象MyFrameCallbackProvider

這個類又出現了一個比較重要的參數Choreographer

private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }

  //AnimationHandler 內部類
  private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

      final Choreographer mChoreographer = Choreographer.getInstance();

      @Override
      public void postFrameCallback(Choreographer.FrameCallback callback) {
          mChoreographer.postFrameCallback(callback);
      }
  }

可以看到前面回調函數中的重複調用的postFrameCallback方法中調用了Choreographer類中的postFrameCallback方法。

postFrameCallback方法中又調用了postFrameCallbackDelayed方法。

postFrameCallbackDelayed方法中又調用了postCallbackDelayedInternal方法。

  public void postFrameCallback(FrameCallback callback) {
      postFrameCallbackDelayed(callback, 0);
  }


  public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
      if (callback == null) {
          throw new IllegalArgumentException("callback must not be null");
      }

      postCallbackDelayedInternal(CALLBACK_ANIMATION,
              callback, FRAME_CALLBACK_TOKEN, delayMillis);
  }

  private void postCallbackDelayedInternal(int callbackType,
                                           Object action, Object token, long delayMillis) {
      ......                
      synchronized (mLock) {
          final long now = SystemClock.uptimeMillis();
          final long dueTime = now + delayMillis;
          mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

          if (dueTime <= now) {
              scheduleFrameLocked(now);
          } else {
              Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
              msg.arg1 = callbackType;
              msg.setAsynchronous(true);
              mHandler.sendMessageAtTime(msg, dueTime);
          }
      }
  }

最終代碼走到了scheduleFrameLocked(now)中,首先看到一個布爾值USE_VSYNC,這個值是用來表示是否開啓垂直同步。


// Enable/disable vsync for animations and drawing.
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769497)
    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync", true);

private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

如果開啓了垂直同步,則判斷當前是否在Looper線程上運行,如果在Looper線程上運行,那麼立即調度vsync,否則,從UI線程發佈一條消息來調度vsync

FrameDisplayEventReceiver類中的scheduleVsync方法就是向native層註冊監聽垂直同步信號,其中參數mReceiverPtr就是一個jni層指向DisplayEventReceiver(的子類FrameDisplayEventReceiver)的指針,jni方法會回調FrameDisplayEventReceiveronVsync方法。

從UI線程發佈一條消息來調度vsync最後也是執行了scheduleVsyncLocked方法。


    private void scheduleFrameLocked(long now) {
        .......
        if (isRunningOnLooperThreadLocked()) {
            scheduleVsyncLocked();
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtFrontOfQueue(msg);
        }        
        ......
    }

    private final class FrameHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           switch (msg.what) {
               .......
               case MSG_DO_SCHEDULE_VSYNC:
                   doScheduleVsync();
                   break;
               .......
           }
       }
   }

   void doScheduleVsync() {
        synchronized (mLock) {
            if (mFrameScheduled) {
                scheduleVsyncLocked();
            }
        }
    }

    private boolean isRunningOnLooperThreadLocked() {
        return Looper.myLooper() == mLooper;
    }

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

// FrameDisplayEventReceiver類中
    @UnsupportedAppUsage
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            nativeScheduleVsync(mReceiverPtr);
        }
    }

收到垂直同步信號回調之後,onVsync方法被回調,這個方法的最後執行了發送HandleMessage.obtain(mHandler, this)),執行了自己的run方法。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {

        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            ......
            mTimestampNanos = timestampNanos;
            mFrame = 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);
        }
    }

run方法中調用了doFramedoFrame方法裏面各種執行doCallbacks方法。

doCallbacks方法中先獲取之前保存的callback,然後通過run方法執行callback,

    @UnsupportedAppUsage
    void doFrame(long frameTimeNanos, int frame) {
        ......
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        ......
    }


    void doCallbacks(int callbackType, long frameTimeNanos) {
       CallbackRecord callbacks;
       synchronized (mLock) {
            animation to start.
           final long now = System.nanoTime();
           //先獲取之前保存的callback
           callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                   now / TimeUtils.NANOS_PER_MS);
           if (callbacks == null) {
               return;
           }
           mCallbacksRunning = true;

       ......

       try {
           Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
           for (CallbackRecord c = callbacks; c != null; c = c.next) {
             //執行callback
               c.run(frameTimeNanos);
           }
       } finally {
           synchronized (mLock) {
               mCallbacksRunning = false;
               do {
                   final CallbackRecord next = callbacks.next;
                   recycleCallbackLocked(callbacks);
                   callbacks = next;
               } while (callbacks != null);
           }
           Trace.traceEnd(Trace.TRACE_TAG_VIEW);
       }
   }

callback方法是從postCallbackDelayedInternal方法中加入隊列的,所以上面的c就是回調方法。

private void postCallbackDelayedInternal(int callbackType,
           Object action, Object token, long delayMillis) {
       ......
           mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
       ......
   }

而這裏的callback方法,就是Choreographer.FrameCallback,這就有回到了這裏。

這個回調中先執行doAnimationFrame方法,再接續註冊回調方法。

而在doAnimationFrame方法中比較重要的是這句callback.doAnimationFrame(frameTime),這裏的callback就是ValueAnimator,這就回到了ValueAnimator中的doAnimationFrame方法。

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

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)) {
                callback.doAnimationFrame(frameTime);
                if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }
            }
        }
        cleanUpList();
    }

//ValueAnimator 中
    getAnimationHandler().addAnimationFrameCallback(this, delay)

ValueAnimator中的doAnimationFrame方法。

一直深入進入,終於看到之前保存的mValues的使用,它被賦上了進度值,,這個方法中還回調了監聽。

* @hide
   */
  public final boolean doAnimationFrame(long frameTime) {
      ......
      boolean finished = animateBasedOnTime(currentTime);

      if (finished) {
          endAnimation();
      }
      return finished;
  }

  boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
            ......
            animateValue(currentIterationFraction);
        }
        return done;
    }

  void animateValue(float fraction) {
       fraction = mInterpolator.getInterpolation(fraction);
       mCurrentFraction = fraction;
       int numValues = mValues.length;
       for (int i = 0; i < numValues; ++i) {
           mValues[i].calculateValue(fraction);
       }
       if (mUpdateListeners != null) {
           int numListeners = mUpdateListeners.size();
           for (int i = 0; i < numListeners; ++i) {
               mUpdateListeners.get(i).onAnimationUpdate(this);
           }
       }
   }

但是貌似還是少了和目標View關聯。

ObjectAnimator類中的animateValue方法,有這樣一句mValues[i].setAnimatedValue(target)關鍵語句

我們知道mValues中保存的是FloatPropertyValuesHolder對象,就是我們所保存的各個屬性值


@Override
   void animateValue(float fraction) {
       final Object target = getTarget();
       if (mTarget != null && target == null) {
           // We lost the target reference, cancel and clean up. Note: we allow null target if the
           /// target has never been set.
           cancel();
           return;
       }

       super.animateValue(fraction);
       int numValues = mValues.length;
       for (int i = 0; i < numValues; ++i) {
           mValues[i].setAnimatedValue(target);
       }
   }

FloatPropertyValuesHolder類中setAnimatedValue中,mSetter不爲空的時候利用反射執行了set方法。


   void setAnimatedValue(Object target) {
       if (mProperty != null) {
           mProperty.set(target, getAnimatedValue());
       }
       if (mSetter != null) {
           try {
               mTmpValueArray[0] = getAnimatedValue();
               mSetter.invoke(target, mTmpValueArray);
           } catch (InvocationTargetException e) {
               Log.e("PropertyValuesHolder", e.toString());
           } catch (IllegalAccessException e) {
               Log.e("PropertyValuesHolder", e.toString());
           }
       }
   }

初始化Setter方法

前面說到了三個重要的方法,現在說第二個startAnimation()

startAnimation()方法中執行了initAnimation()initAnimation()方法中調用了PropertyValuesHolder類的init()方法。

也沒有什麼特別的地方

   private void startAnimation() {
       if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
           Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                   System.identityHashCode(this));
       }

       mAnimationEndRequested = false;
       initAnimation();
       mRunning = true;
       if (mSeekFraction >= 0) {
           mOverallFraction = mSeekFraction;
       } else {
           mOverallFraction = 0f;
       }
       if (mListeners != null) {
           notifyStartListeners();
       }
   }

   @CallSuper
    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }

//  PropertyValuesHolder
    void init() {
       if (mEvaluator == null) {
           // We already handle int and float automatically, but not their Object
           // equivalents
           mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                   (mValueType == Float.class) ? sFloatEvaluator :
                   null;
       }
       if (mEvaluator != null) {
           // KeyframeSet knows how to evaluate the common types - only give it a custom
           // evaluator if one has been set on this class
           mKeyframes.setEvaluator(mEvaluator);
       }
   }

再看ObjectAnimatorinitAnimation方法,會調用 FloatPropertyValuesHolder.setupSetterAndGetter(target)

這個方法中對之前沒有找到的mSetter進行了賦值。

@Override
    void initAnimation() {
        if (!mInitialized) {
            // mValueType may change due to setter/getter setup; do this before calling super.init(),
            // which uses mValueType to set up the default type evaluator.
            final Object target = getTarget();
            if (target != null) {
                final int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            super.initAnimation();
        }
    }

//  FloatPropertyValuesHolder 類中
  void setupSetterAndGetter(Object target) {
      if (mProperty != null) {
          // check to make sure that mProperty is on the class of target
          try {
              Object testValue = null;
              List<Keyframe> keyframes = mKeyframes.getKeyframes();
              int keyframeCount = keyframes == null ? 0 : keyframes.size();
              for (int i = 0; i < keyframeCount; i++) {
                  Keyframe kf = keyframes.get(i);
                  if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                      if (testValue == null) {
                          testValue = convertBack(mProperty.get(target));
                      }
                      kf.setValue(testValue);
                      kf.setValueWasSetOnStart(true);
                  }
              }
              return;
          } catch (ClassCastException e) {
              Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                      ") on target object " + target + ". Trying reflection instead");
              mProperty = null;
          }
      }
      // We can't just say 'else' here because the catch statement sets mProperty to null.
      if (mProperty == null) {
          Class targetClass = target.getClass();
          if (mSetter == null) {
              setupSetter(targetClass);
          }
          List<Keyframe> keyframes = mKeyframes.getKeyframes();
          int keyframeCount = keyframes == null ? 0 : keyframes.size();
          for (int i = 0; i < keyframeCount; i++) {
              Keyframe kf = keyframes.get(i);
              if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                  if (mGetter == null) {
                      setupGetter(targetClass);
                      if (mGetter == null) {
                          // Already logged the error - just return to avoid NPE
                          return;
                      }
                  }
                  try {
                      Object value = convertBack(mGetter.invoke(target));
                      kf.setValue(value);
                      kf.setValueWasSetOnStart(true);
                  } catch (InvocationTargetException e) {
                      Log.e("PropertyValuesHolder", e.toString());
                  } catch (IllegalAccessException e) {
                      Log.e("PropertyValuesHolder", e.toString());
                  }
              }
          }
      }
  }

  void setupSetter(Class targetClass) {
       Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
       mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
   }

有了mSetter這個參數動畫效果就能執行起來了。

往set方法總設置值

最後看看最後第一個關鍵方法setCurrentPlayTime(0)

  public void setCurrentPlayTime(long playTime) {
      float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
      setCurrentFraction(fraction);
  }

 public void setCurrentFraction(float fraction) {
     initAnimation();
     fraction = clampFraction(fraction);
     mStartTimeCommitted = true; // do not allow start time to be compensated for jank
     if (isPulsingInternal()) {
         long seekTime = (long) (getScaledDuration() * fraction);
         long currentTime = AnimationUtils.currentAnimationTimeMillis();
         // Only modify the start time when the animation is running. Seek fraction will ensure
         // non-running animations skip to the correct start time.
         mStartTime = currentTime - seekTime;
     } else {
         // If the animation loop hasn't started, or during start delay, the startTime will be
         // adjusted once the delay has passed based on seek fraction.
         mSeekFraction = fraction;
     }
     mOverallFraction = fraction;
     final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
     animateValue(currentIterationFraction);
 }

會調用到animateValue(currentIterationFraction)方法,我們知道這個方法就是對view屬性的更新,所以可以知道在調用了startAnimation()之後,就會立即進行一次view的更新操作,此時參數fraction爲0。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章