自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

屬性動(dòng)畫機(jī)制原理解析

移動(dòng)開發(fā) Android
動(dòng)畫的使用是 Android 開發(fā)中常用的知識,可是動(dòng)畫的種類繁多、使用復(fù)雜,每當(dāng)需要采用自定義動(dòng)畫 實(shí)現(xiàn) 復(fù)雜的動(dòng)畫效果時(shí),很多開發(fā)者就顯得束手無策。

[[437959]]

本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程」,作者Android開發(fā)編程 。轉(zhuǎn)載本文請聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號。

前言

動(dòng)畫的使用是 Android 開發(fā)中常用的知識

可是動(dòng)畫的種類繁多、使用復(fù)雜,每當(dāng)需要采用自定義動(dòng)畫 實(shí)現(xiàn) 復(fù)雜的動(dòng)畫效果時(shí),很多開發(fā)者就顯得束手無策;

今天我們就來從源碼中分析屬性動(dòng)畫原理

一、動(dòng)畫簡單應(yīng)用

ValueAnimator

屬性動(dòng)畫的最核心的類,原理:控制值的變化,之后手動(dòng)賦值給對象的屬性,從而實(shí)現(xiàn)動(dòng)畫;

對于控制的值的不同,Android 提供給我們?nèi)N構(gòu)造方法來實(shí)例ValueAnimator對象:

ValueAnimator.ofInt(int... values) -- 整型數(shù)值

ValueAnimator.ofFloat(float... values) -- 浮點(diǎn)型數(shù)值

ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values) -- 自定義對象類型

1、java方式

  1. //設(shè)置動(dòng)畫 始 & 末值 
  2.                 //ofInt()兩個(gè)作用: 
  3.                 //1. 獲取實(shí)例 
  4.                 //2. 在傳入?yún)?shù)之間平滑過渡 
  5.                 //如下則0平滑過渡到3 
  6.                 ValueAnimator animator = ValueAnimator.ofInt(0,3); 
  7.                 //如下傳入多個(gè)參數(shù),效果則為0->5,5->3,3->10 
  8.                 //ValueAnimator animator = ValueAnimator.ofInt(0,5,3,10); 
  9.                 //設(shè)置動(dòng)畫的基礎(chǔ)屬性 
  10.                 animator.setDuration(5000);//播放時(shí)長 
  11.                 animator.setStartDelay(300);//延遲播放 
  12.                 animator.setRepeatCount(0);//重放次數(shù) 
  13.                 animator.setRepeatMode(ValueAnimator.RESTART); 
  14.                 //重放模式 
  15.                 //ValueAnimator.START:正序 
  16.                 //ValueAnimator.REVERSE:倒序 
  17.                 //設(shè)置更新監(jiān)聽 
  18.                 //值 改變一次,該方法就執(zhí)行一次 
  19.                 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
  20.                     @Override 
  21.                     public void onAnimationUpdate(ValueAnimator animation) { 
  22.                         //獲取改變后的值 
  23.                         int currentValue = (int) animation.getAnimatedValue(); 
  24.                         //輸出改變后的值 
  25.                         Log.d("test""onAnimationUpdate: " + currentValue); 
  26.                         //改變后的值發(fā)賦值給對象的屬性值 
  27.                         view.setproperty(currentValue); 
  28.                         //刷新視圖 
  29.                         view.requestLayout(); 
  30.                     } 
  31.                 }); 
  32.                 //啟動(dòng)動(dòng)畫 
  33.                 animator.start(); 

2、 XML 方式

在路徑 res/animator/ 路徑下常見 XML 文件,如 set_animator.xml

在上述文件中設(shè)置動(dòng)畫參數(shù)

  1. // ValueAnimator采用<animator>  標(biāo)簽 
  2. <animator xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:duration="1000" 
  4.     android:valueFrom="1" 
  5.     android:valueTo="0" 
  6.     android:valueType="floatType" 
  7.     android:repeatCount="1" 
  8.     android:repeatMode="reverse"/> 
  9. /> 

Java代碼啟動(dòng)動(dòng)畫

  1. Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);   
  2. // 載入XML動(dòng)畫 
  3. animator.setTarget(view);   
  4. // 設(shè)置動(dòng)畫對象 
  5. animator.start();   

二、原理詳解

1、創(chuàng)建動(dòng)畫

  1. ObjectAnimator.ofFloat()開始; 
  2.     /** 
  3.      * 構(gòu)建一個(gè)返回值為 float 的 ObjectAnimator 的實(shí)例 
  4.      * 
  5.      * @param target 作用于動(dòng)畫的對象。 
  6.      * @param propertyName 屬性名稱,要求對象須有setXXX() 方法,且是 public 的。 
  7.      * @param values,屬性變化的值,可以設(shè)置  1 個(gè)或者 多個(gè)。當(dāng)只有 1 個(gè)時(shí),起始值為屬性值本身。當(dāng)有 2 個(gè)值時(shí),第 1 個(gè)為起始值,第 2 個(gè)為終止值。當(dāng)超過 2 個(gè)時(shí),首尾值的定義與 2 個(gè)時(shí)一樣,中間值做需要經(jīng)過的值。 
  8.      */ 
  9. public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { 
  10.         ObjectAnimator anim = new ObjectAnimator(target, propertyName); 
  11.         anim.setFloatValues(values); 
  12.         return anim; 
  13.     } 
  • 創(chuàng)建一個(gè) ObjectAnimator 的實(shí)例,然后為該實(shí)例設(shè)置 values;
  • 那么,繼續(xù)看 ObjectAnimator 的構(gòu)建;

構(gòu)造 ObjectaAnimator

  1. private ObjectAnimator(Object target, String propertyName) { 
  2.         setTarget(target); 
  3.         setPropertyName(propertyName); 
  4.     } 

分別調(diào)用了 setTarget() 方法和setPropertyName();

2、setTarget()

  1. public void setTarget(@Nullable Object target) { 
  2.         final Object oldTarget = getTarget(); 
  3.         if (oldTarget != target) { 
  4.             if (isStarted()) { 
  5.                 cancel(); 
  6.             } 
  7.             mTarget = target == null ? null : new WeakReference<Object>(target); 
  8.             // New target should cause re-initialization prior to starting 
  9.             mInitialized = false
  10.         } 
  11.     } 

存在舊動(dòng)畫對象(也可為 null) 與新設(shè)置的動(dòng)畫對象不一致;

如果舊動(dòng)畫是開始了的狀態(tài),則先取消動(dòng)畫,然后將動(dòng)畫對象以弱引用對象為記錄下來;

3、setPropertyName()

  1. public void setPropertyName(@NonNull String propertyName) { 
  2.         // mValues could be null if this is being constructed piecemeal. Just record the 
  3.         // propertyName to be used later when setValues() is called if so. 
  4.         if (mValues != null) { 
  5.             PropertyValuesHolder valuesHolder = mValues[0]; 
  6.             String oldName = valuesHolder.getPropertyName(); 
  7.             valuesHolder.setPropertyName(propertyName); 
  8.             mValuesMap.remove(oldName); 
  9.             mValuesMap.put(propertyName, valuesHolder); 
  10.         } 
  11.         mPropertyName = propertyName; 
  12.         // New property/values/target should cause re-initialization prior to starting 
  13.         mInitialized = false
  14.     } 
  • 記錄下 propertyName 的名字;
  • 而如果已經(jīng)有這個(gè) propertyName,則會替換其相應(yīng)的 PropertyValuesHolder,這里用了一個(gè) HashMap 來保存 propertyName 和 PropertyValuesHolder
  • 如果propertyName 是 "translationX";
  • 接下來看 setFloatValues() 方法;

4、setFloatValues()

  1. @Override 
  2.     public void setFloatValues(float... values) { 
  3.         if (mValues == null || mValues.length == 0) { 
  4.             // 當(dāng)前還沒有任何值 
  5.             if (mProperty != null) { 
  6.                 setValues(PropertyValuesHolder.ofFloat(mProperty, values)); 
  7.             } else { 
  8.                 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); 
  9.             } 
  10.         } else { 
  11.             // 當(dāng)前已經(jīng)有值的情況,調(diào)用父類的 setFloatValues() 
  12.             super.setFloatValues(values); 
  13.         } 
  14.     } 

父類,即 ValueAnimator ,其方法setFloatValues() 如下;

5、ValueAnimator#setFloatValues()

  1. public void setFloatValues(float... values) { 
  2.         if (values == null || values.length == 0) { 
  3.             return
  4.         } 
  5.         if (mValues == null || mValues.length == 0) { 
  6.             setValues(PropertyValuesHolder.ofFloat(""values)); 
  7.         } else { 
  8.             PropertyValuesHolder valuesHolder = mValues[0]; 
  9.             valuesHolder.setFloatValues(values); 
  10.         } 
  11.         // New property/values/target should cause re-initialization prior to starting 
  12.         mInitialized = false
  13.     } 
  • 不管是否調(diào)用父類的 setFloatValues();
  • 最后都是要將 values 逐個(gè)構(gòu)造成 PropertyValuesHolder,最后存放在前面所說的 HashMap 里面;
  • 當(dāng)然,如果這里的 hashMap 還沒有初始化,則先會將其初始化;
  • 最關(guān)鍵的是要構(gòu)建出 PropertyValuesHolder 這個(gè)對象;
  • 那么就繼續(xù)來看看 PropertyValuesHolder#ofFloat() 方法;

6、PropertyValuesHolder#ofFloat()

  1.  public static PropertyValuesHolder ofFloat(String propertyName, float... values) { 
  2.         return new FloatPropertyValuesHolder(propertyName, values); 
  3.     } 
  4. 構(gòu)造 FloatPropertyValuesHolder; 
  5. FloatPropertyValuesHolder 
  6.         public FloatPropertyValuesHolder(String propertyName, float... values) { 
  7.             super(propertyName); 
  8.             setFloatValues(values); 
  9.         } 
  • FloatPropertyValuesHolder 構(gòu)造函數(shù)比較簡單,調(diào)用父類的構(gòu)造方法并傳遞了 propertyName;
  • 關(guān)鍵是進(jìn)一步 setFloatValues() 方法的調(diào)用;
  • 其又進(jìn)一步調(diào)用了父類的 setFloatValues(),在父類的 setFloatValues() 方法里初始化了動(dòng)畫的關(guān)鍵幀;
  1. PropertyValuesHolder#setFloatValues() 
  2.     public void setFloatValues(float... values) { 
  3.         mValueType = float.class; 
  4.         mKeyframes = KeyframeSet.ofFloat(values); 
  5.     } 
  • 進(jìn)一步調(diào)用了 KeyframeSet#ofFloat() 方法以完成關(guān)鍵幀的構(gòu)造;
  • KeyframeSet 是接口 Keyframe 的實(shí)現(xiàn)類;

7、KeyframeSet#ofFloat()

  1. public static KeyframeSet ofFloat(float... values) { 
  2.         boolean badValue = false
  3.         int numKeyframes = values.length; 
  4.         // 至少要 2 幀 
  5.         FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; 
  6.         // 然后構(gòu)造出每一幀,每一幀中主要有 2 個(gè)重要的參數(shù) fraction 以及 value 
  7.         if (numKeyframes == 1) { 
  8.             keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); 
  9.             keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); 
  10.             if (Float.isNaN(values[0])) { 
  11.                 badValue = true
  12.             } 
  13.         } else { 
  14.             keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); 
  15.             for (int i = 1; i < numKeyframes; ++i) { 
  16.                 keyframes[i] = 
  17.                         (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]); 
  18.                 if (Float.isNaN(values[i])) { 
  19.                     badValue = true
  20.                 } 
  21.             } 
  22.         } 
  23.         if (badValue) { 
  24.             Log.w("Animator""Bad value (NaN) in float animator"); 
  25.         } 
  26.         // 最后將所有的 關(guān)鍵幀 匯集到一個(gè)集合中 
  27.         return new FloatKeyframeSet(keyframes); 
  28.     } 

其主要內(nèi)容是:

  • 構(gòu)造動(dòng)畫的關(guān)鍵幀,且動(dòng)畫里至少要有 2 個(gè)關(guān)鍵幀;
  • 關(guān)鍵幀中有 2 個(gè)重要的參數(shù),fraction這個(gè)可以看成是關(guān)鍵幀的序號,value 關(guān)鍵幀的值,可能是起始值,也可能是中間的某個(gè)值;
  • 最后將關(guān)鍵幀匯集成一個(gè)關(guān)鍵幀集返回給 PropertyValuesHolder;

8、setDuration()

  1. @Override 
  2.     @NonNull 
  3.     public ObjectAnimator setDuration(long duration) { 
  4.         super.setDuration(duration); 
  5.         return this; 
  6.     } 

調(diào)用了父類 ValueAnimator 的 setDuration();

  1. ValueAnimator#setDuration() 
  2.     @Override 
  3.     public ValueAnimator setDuration(long duration) { 
  4.         if (duration < 0) { 
  5.             throw new IllegalArgumentException("Animators cannot have negative duration: " + 
  6.                     duration); 
  7.         } 
  8.         mDuration = duration; 
  9.         return this; 
  10.     } 

setDuration() 只是簡單的存儲下 duration 的值,僅此而已,那么繼續(xù)分析 setInterpolator();

9、setInterpolator()

  1. @Override 
  2.     public void setInterpolator(TimeInterpolator value) { 
  3.         if (value != null) { 
  4.             mInterpolator = value; 
  5.         } else { 
  6.             mInterpolator = new LinearInterpolator(); 
  7.         } 
  8.     } 

傳遞的是 null 的話,則默認(rèn)使用的便是 LinearInterpolator,即線性插值器;

我們這里的假設(shè)的場景也是設(shè)置了 LinearInterpolator,這是最簡單的插值器,其作用就是完成勻速運(yùn)動(dòng);

10、 LinearInterpolator粗略分析;

  1. /** 
  2.  * 插值器定義了動(dòng)畫變化的頻率,其可以是線性的也可以是非線性的,如加速運(yùn)動(dòng)或者減速運(yùn)動(dòng); 
  3.  */ 
  4. public interface TimeInterpolator { 
  5.     /** 
  6.      * 這里傳進(jìn)來的 input 代表當(dāng)前時(shí)間與總時(shí)間的比,根據(jù)這個(gè)時(shí)間占比返回當(dāng)前的變化頻率。其輸出與輸值都在 [0,1] 之間 
  7.      */ 
  8.     float getInterpolation(float input); 

插值器的關(guān)鍵定義便是實(shí)現(xiàn) getInterpolation() 方法,即根據(jù)當(dāng)前動(dòng)畫運(yùn)行的時(shí)間占比來計(jì)算當(dāng)前動(dòng)畫的變化頻率;

那么來看看 LinearInterpolator 的 getInterpolation() 實(shí)現(xiàn);

  1. LinearInterpolator#getInterpolation() 
  2. public float getInterpolation(float input) { 
  3.         return input; 
  4.     } 

對,就是返回原值,因?yàn)闀r(shí)間的變化肯定始終都是勻速的;

11、start

啟動(dòng)動(dòng)畫從 start() 方法開始

  1. @Override 
  2.     public void start() { 
  3.         AnimationHandler.getInstance().autoCancelBasedOn(this); 
  4.         if (DBG) { 
  5.             Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); 
  6.             for (int i = 0; i < mValues.length; ++i) { 
  7.                 PropertyValuesHolder pvh = mValues[i]; 
  8.                 Log.d(LOG_TAG, "   Values[" + i + "]: " + 
  9.                     pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + 
  10.                     pvh.mKeyframes.getValue(1)); 
  11.             } 
  12.         } 
  13.         super.start(); 
  14.     } 
  • 先確認(rèn)動(dòng)畫已經(jīng)取消;這個(gè)方法里的重要的那句代碼就是調(diào)用父類 ValueAnimator 的 start();
  • 父類對外的 start() 方法很簡單,其主要的實(shí)現(xiàn)在另一個(gè)重載的私有 start() 方法上;
  1. private void start(boolean playBackwards) { 
  2.        ..... 
  3.         mReversing = playBackwards; 
  4.         // 重置脈沖為 "true" 
  5.         mSelfPulse = !mSuppressSelfPulseRequested; 
  6.         ..... 
  7.         // 添加脈沖回調(diào)用 
  8.         addAnimationCallback(0); 
  9.         if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) { 
  10.             // If there's no start delay, init the animation and notify start listeners right away 
  11.             // to be consistent with the previous behavior. Otherwise, postpone this until the first 
  12.             // frame after the start delay. 
  13.             startAnimation(); 
  14.             if (mSeekFraction == -1) { 
  15.                 // No seek, start at play time 0. Note that the reason we are not using fraction 0 
  16.                 // is because for animations with 0 duration, we want to be consistent with pre-N 
  17.                 // behavior: skip to the final value immediately. 
  18.                 setCurrentPlayTime(0); 
  19.             } else { 
  20.                 setCurrentFraction(mSeekFraction); 
  21.             } 
  22.         } 
  23.     } 

其中之一是 addAnimationCallback(),其主要是向 AnimationHander 添加一個(gè)回調(diào)接口AnimationHandler.AnimationFrameCallback,如下代碼;

  1. /** 
  2.      * Register to get a callback on the next frame after the delay. 
  3.      */ 
  4.     public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) { 
  5.         if (mAnimationCallbacks.size() == 0) { 
  6.             getProvider().postFrameCallback(mFrameCallback); 
  7.         } 
  8.         if (!mAnimationCallbacks.contains(callback)) { 
  9.             mAnimationCallbacks.add(callback); 
  10.         } 
  11.         if (delay > 0) { 
  12.             mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay)); 
  13.         } 
  14.     } 
  • ValueAnimator 就實(shí)現(xiàn)了 AnimationFrameCallback,所以這里添加的是 ValueAnimator 的實(shí)例;
  • 最終被添加到 mAnimationCallbacks 這個(gè)隊(duì)列中;

12、startAnimation()

  1. private void startAnimation() { 
  2.         ...... 
  3.         mAnimationEndRequested = false
  4.         initAnimation(); 
  5.         mRunning = true
  6.         if (mSeekFraction >= 0) { 
  7.             mOverallFraction = mSeekFraction; 
  8.         } else { 
  9.             mOverallFraction = 0f; 
  10.         } 
  11.         if (mListeners != null) { 
  12.             // 通過動(dòng)畫監(jiān)聽器動(dòng)畫開始了 
  13.             notifyStartListeners(); 
  14.         } 
  15.     } 

關(guān)鍵調(diào)用 initAnimation()

  1. void initAnimation() { 
  2.         if (!mInitialized) { 
  3.             int numValues = mValues.length; 
  4.             for (int i = 0; i < numValues; ++i) { 
  5.                 mValues[i].init(); 
  6.             } 
  7.             mInitialized = true
  8.         } 
  9.     } 

mValues 是 PropertyValuesHolder 數(shù)組,這里的目的是初始化 PropertyValuesHolder;

  1. void init() { 
  2.         if (mEvaluator == null) { 
  3.             // We already handle int and float automatically, but not their Object 
  4.             // equivalents 
  5.             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : 
  6.                     (mValueType == Float.class) ? sFloatEvaluator : 
  7.                     null
  8.         } 
  9.         if (mEvaluator != null) { 
  10.             // KeyframeSet knows how to evaluate the common types - only give it a custom 
  11.             // evaluator if one has been set on this class 
  12.             mKeyframes.setEvaluator(mEvaluator); 
  13.         } 
  14.     } 
  • init() 方法的主要目的是就是給關(guān)鍵幀設(shè)置估值器;
  • 前面調(diào)用的是 ObjectAnimator#ofFloat() 方法,所以這里默認(rèn)給的就是 FloatEvaluator;
  • 接下來就會進(jìn)一步調(diào)用 setCurrentPlayTime() 來開始動(dòng)畫;

13、setCurrentPlayTime()

  1. public void setCurrentPlayTime(long playTime) { 
  2.         float fraction = mDuration > 0 ? (float) playTime / mDuration : 1; 
  3.         setCurrentFraction(fraction); 
  4.     } 
  • 初始時(shí)調(diào)用的是setCurrentPlayTime(0),也就是 playTime 為 0,而 mDuration 就是我們自己通過 setDuration() 來設(shè)置的;
  • 所以這里得到的 fraction 也是 0;
  • 進(jìn)一步看 setCurrentFraction() 方法;

14、setCurrentFraction

  1. public void setCurrentFraction(float fraction) { 
  2.         // 再次調(diào)用 initAnimation() ,前面初始化過了,所以這里是無用的 
  3.         initAnimation(); 
  4.         // 校準(zhǔn) fraction 為 [0, mRepeatCount + 1] 
  5.         fraction = clampFraction(fraction); 
  6.         mStartTimeCommitted = true; // do not allow start time to be compensated for jank 
  7.         if (isPulsingInternal()) { 
  8.             // 隨機(jī)時(shí)間? 
  9.             long seekTime = (long) (getScaledDuration() * fraction); 
  10.             // 獲取動(dòng)畫的當(dāng)前運(yùn)行時(shí)間 
  11.             long currentTime = AnimationUtils.currentAnimationTimeMillis(); 
  12.             // Only modify the start time when the animation is running. Seek fraction will ensure 
  13.             // non-running animations skip to the correct start time
  14.             // 得到開始時(shí)間 
  15.             mStartTime = currentTime - seekTime; 
  16.         } else { 
  17.             // If the animation loop hasn't started, or during start delay, the startTime will be 
  18.             // adjusted once the delay has passed based on seek fraction. 
  19.             mSeekFraction = fraction; 
  20.         } 
  21.         mOverallFraction = fraction; 
  22.         final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing); 
  23.         // 執(zhí)行動(dòng)畫,注意這里會先調(diào)用子類的 animateValue() 方法 
  24.         animateValue(currentIterationFraction); 
  25.     } 

前面都是一些時(shí)間的計(jì)算,得到當(dāng)前真正的currentIterationFraction,最后會通過調(diào)用animateValue() 來執(zhí)行動(dòng)畫;

15、 ObjectAnimator#animateValue()

  1. void animateValue(float fraction) { 
  2.         final Object target = getTarget(); 
  3.         if (mTarget != null && target == null) { 
  4.             // We lost the target reference, cancel and clean up. Note: we allow null target if the 
  5.             /// target has never been set
  6.             cancel(); 
  7.             return
  8.         } 
  9.         // 調(diào)用父類的 animateValue() ,這個(gè)很關(guān)鍵,時(shí)間插值與估值器的計(jì)算都在父類的 animateValue() 方法中進(jìn)行的。 
  10.         super.animateValue(fraction); 
  11.         int numValues = mValues.length; 
  12.         for (int i = 0; i < numValues; ++i) { 
  13.             // 這里的 mValues 的是PropertyValuesHolder[],也就是在 PropertyValuesHolder 里面來改變了目標(biāo) target 的屬性值。 
  14.             mValues[i].setAnimatedValue(target); 
  15.         } 
  16.     } 

父類 ValueAnimator#animateValue()

  1. void animateValue(float fraction) { 
  2.         // 獲取時(shí)間插值 
  3.         fraction = mInterpolator.getInterpolation(fraction); 
  4.         mCurrentFraction = fraction; 
  5.         int numValues = mValues.length; 
  6.         // 將時(shí)間插值送給估值器,計(jì)算出 values 
  7.         for (int i = 0; i < numValues; ++i) { 
  8.             mValues[i].calculateValue(fraction); 
  9.         } 
  10.        // 發(fā)出通知 
  11.         if (mUpdateListeners != null) { 
  12.             int numListeners = mUpdateListeners.size(); 
  13.             for (int i = 0; i < numListeners; ++i) { 
  14.                 mUpdateListeners.get(i).onAnimationUpdate(this); 
  15.             } 
  16.         } 
  17.     } 

animateValue(): 計(jì)算時(shí)間插值和估值器、調(diào)用 PropertyValuesHolder 來改變屬性;

  1. void setAnimatedValue(Object target) { 
  2.         if (mProperty != null) { 
  3.             mProperty.set(target, getAnimatedValue()); 
  4.         } 
  5.         if (mSetter != null) { 
  6.             try { 
  7.                 mTmpValueArray[0] = getAnimatedValue(); 
  8.                 // 通過反射調(diào)用來修改屬性值 
  9.                 mSetter.invoke(target, mTmpValueArray); 
  10.             } catch (InvocationTargetException e) { 
  11.                 Log.e("PropertyValuesHolder", e.toString()); 
  12.             } catch (IllegalAccessException e) { 
  13.                 Log.e("PropertyValuesHolder", e.toString()); 
  14.             } 
  15.         } 
  16.     } 
  • 這里就是通過屬性的 Setter 方法來修改屬性的;
  • 分析到這里,就完成了動(dòng)畫的一幀關(guān)鍵幀的執(zhí)行;
  • 剩下的幀是怎么驅(qū)動(dòng)的呢?還是得回到 start() 方法里面,在這里最初分析到 addAnimationFrameCallback() 方法;
  • 這個(gè)方法里等于是向AnimationHandler注冊了AnimationHandler.AnimationFrameCallback;
  • 這個(gè) callback 中其中之一的方法是 doAnimationFrame();

在 ValueAnimator 的實(shí)現(xiàn)中如下;

  1. public final boolean doAnimationFrame(long frameTime) { 
  2.         ..... 
  3.         boolean finished = animateBasedOnTime(currentTime); 
  4.         if (finished) { 
  5.             endAnimation(); 
  6.         } 
  7.         return finished; 
  8.     } 

這段代碼原來也是很長的,我們只看關(guān)鍵調(diào)用 animateBasedOnTime()

  1. boolean animateBasedOnTime(long currentTime) { 
  2.         boolean done = false
  3.         if (mRunning) { 
  4.             ..... 
  5.             float currentIterationFraction = getCurrentIterationFraction( 
  6.                     mOverallFraction, mReversing); 
  7.             animateValue(currentIterationFraction); 
  8.         } 
  9.         return done; 
  10.     } 
  • 目的也還是計(jì)算出 currentIterationFraction;
  • 通過 animateValue() 方法來執(zhí)行動(dòng)畫;
  • 可以看到只要 doAnimationFrame() 被不斷的調(diào)用,就會產(chǎn)生動(dòng)畫的一個(gè)關(guān)鍵幀;
  • 如果關(guān)鍵幀是連續(xù)的,那么最后也就產(chǎn)生了我們所看到的動(dòng)畫;
  • 再來分析doAnimationFrame() 是如何被不斷調(diào)用的;
  • 這個(gè)需要回到 AnimationHandler 中來,在 AnimationHandler 中有一個(gè)非常重要的 callback 實(shí)現(xiàn)——Choreographer.FrameCallback;
  1. private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { 
  2.         @Override 
  3.         public void doFrame(long frameTimeNanos) { 
  4.             doAnimationFrame(getProvider().getFrameTime()); 
  5.             if (mAnimationCallbacks.size() > 0) { 
  6.                 getProvider().postFrameCallback(this); 
  7.             } 
  8.         } 
  9.     }; 
  • Andorid 中的重繪就是由Choreographer在 1 秒內(nèi)產(chǎn)生 60 個(gè) vsync 來通知 view tree 進(jìn)行 view 的重繪的;
  • 而 vsync 產(chǎn)生后會調(diào)用它的監(jiān)聽者回調(diào)接口 Choreographer.FrameCallback,;
  • 也就是說,只要向Choreographer注冊了這個(gè)接口,就會每 1 秒里收到 60 次回調(diào);
  • 因此,在這里就實(shí)現(xiàn)了不斷地調(diào)用 doAnimationFrame() 來驅(qū)動(dòng)動(dòng)畫了;

16、流程總結(jié)

  • 動(dòng)畫是由許多的關(guān)鍵幀組成的;
  • 屬性動(dòng)畫的主要組成是 PropertyValuesHolder,而 PropertyValuesHolder 又封裝了關(guān)鍵幀;
  • 動(dòng)畫開始后,其監(jiān)聽了 Choreographer 的 vsync,使得其可以不斷地調(diào)用 doAnimationFrame() 來驅(qū)動(dòng)動(dòng)畫執(zhí)行每一個(gè)關(guān)鍵幀;
  • 每一次的 doAnimationFrame() 調(diào)用都會去計(jì)算時(shí)間插值,而通過時(shí)間插值器計(jì)算得到 fraction 又會傳給估值器,使得估值器可以計(jì)算出屬性的當(dāng)前值;
  • 最后再通過 PropertyValuesHolder 所記錄下的 Setter 方法,以反射的方式來修改目標(biāo)屬性的值;
  • 當(dāng)屬性值一幀一幀的改變后,形成連續(xù)后,便是我們所見到的動(dòng)畫;

總結(jié)

2021年最后一個(gè)月,大家一起加油努力;

 

 

責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2017-05-31 13:16:35

PHP運(yùn)行機(jī)制原理解析

2023-02-28 09:07:18

ChatGPTAI

2019-12-06 10:59:20

JavaScript運(yùn)行引擎

2021-07-12 09:45:36

NameServer 核心Conusmer

2021-01-12 14:46:34

Kubernetes開發(fā)存儲

2021-07-05 07:51:43

JVM底層Python

2023-10-13 13:30:00

MySQL鎖機(jī)制

2023-08-11 07:44:40

TCP滑動(dòng)窗口數(shù)據(jù)

2020-05-21 13:25:43

Spring組件架構(gòu)

2021-12-06 14:52:08

動(dòng)畫Android補(bǔ)間動(dòng)畫

2024-06-27 08:26:10

LooperAndroid內(nèi)存

2015-08-18 09:40:32

OpenStack Neutron虛擬網(wǎng)絡(luò)

2023-11-16 09:01:37

Hadoop數(shù)據(jù)庫

2015-08-19 10:36:24

Zigbee技術(shù)無線通信

2015-07-01 13:34:22

Kubernetes應(yīng)用部署模型

2010-07-06 10:07:10

jQueryJSON

2021-06-16 15:18:03

鴻蒙HarmonyOS應(yīng)用

2021-09-04 07:29:57

Android

2025-04-02 07:29:14

2024-10-12 10:29:11

計(jì)算機(jī)圖形
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號