Android屬性動畫基礎:你是否真的瞭解插值器(TimeInterpolator)
插值器和估值器是我們可以改變動畫更新值的兩個切入點,通過自定義插值器和估值器,我們可以隨意改變動畫更新時值的計算方式以滿足我們特定的需求。本文簡單介紹屬性動畫插值器(TimeInterpolator)。在讀此文前,如果您還不瞭解屬性動畫執行流程,建議您先看一下這篇文章,簡單瞭解一下:Android屬性動畫基礎之流程解析
首先,看一下TimeInterpolator原始碼:
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
通過介面描述,可以知道,通過插值器可以更改動畫的變化速率,其實類似於視訊播放,我們可以快放也可以慢放,只不過快放和慢放的速率都是線性的。隨著執行時間的流逝,動畫不斷進行更新,即根據動畫執行的時間來更新其所操縱的數值或物件,getInterpolation(float input)方法中的input引數就是與動畫當前執行週期內執行時間相關的歸一化變數,取值範圍[0,1],這點在上一篇文章Android屬性動畫基礎之流程解析有所提及,只是礙於篇幅沒有詳細介紹,這篇文章會對其做較為詳盡的解析。getInterpolation(float input)方法所計算出的數值會直接作為時間因子參與動畫更新計算。我們先看一下方法api對input的描述,翻譯過來大概是:"input引數取值範圍[0,1],表示動畫當前所處的節點,0代表動畫開始,1代表動畫結束"。但是,可但是,但可是,這個描述其實是不嚴謹的,稍後我們分析input引數的數值計算方式就會知道為何這個描述是不嚴謹的。
為了搞清楚上述input引數的計算方式,我們需要知道getInterpolation方法何時被觸發,不用想,肯定是計算更新之前被觸發的,這簡直是廢話,其實我們首先需要了解屬性動畫執行流程(請參考Android屬性動畫基礎之流程解析),這裡不做過多闡述,直接看相關程式碼(如對屬性動畫流程有疑問,:
1 boolean animateBasedOnTime(long currentTime) {
2 boolean done = false;
3 if (mRunning) {
4 final long scaledDuration = getScaledDuration();
5 final float fraction = scaledDuration > 0 ?
6 (float)(currentTime - mStartTime) / scaledDuration : 1f;
7 final float lastFraction = mOverallFraction;
8 final boolean newIteration = (int) fraction > (int) lastFraction;
9 final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
10 (mRepeatCount != INFINITE);
11 if (scaledDuration == 0) {
12 // 0 duration animator, ignore the repeat count and skip to the end
13 done = true;
14 } else if (newIteration && !lastIterationFinished) {
15 // Time to repeat
16 if (mListeners != null) {
17 int numListeners = mListeners.size();
18 for (int i = 0; i < numListeners; ++i) {
19 mListeners.get(i).onAnimationRepeat(this);
20 }
21 }
22 } else if (lastIterationFinished) {
23 done = true;
24 }
25 mOverallFraction = clampFraction(fraction);
26 float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
27 animateValue(currentIterationFraction);
28 }
29 return done;
30 }
31 private float clampFraction(float fraction) {
32 if (fraction < 0) {
33 fraction = 0;
34 } else if (mRepeatCount != INFINITE) {
35 fraction = Math.min(fraction, mRepeatCount + 1);
36 }
37 return fraction;
38 }
/**
* Calculates the fraction of the current iteration, taking into account whether the animation
* should be played backwards. E.g. When the animation is played backwards in an iteration,
* the fraction for that iteration will go from 1f to 0f.
*/
39 private float getCurrentIterationFraction(float fraction) {
40 fraction = clampFraction(fraction);
41 int iteration = getCurrentIteration(fraction);
42 float currentFraction = fraction - iteration;
43 return shouldPlayBackward(iteration) ? 1f - currentFraction : currentFraction;
44 }
/**
* Calculates the direction of animation playing (i.e. forward or backward), based on 1)
* whether the entire animation is being reversed, 2) repeat mode applied to the current
* iteration.
*/
45 private boolean shouldPlayBackward(int iteration) {
46 // 注意此處條件判斷
47 if (iteration > 0 && mRepeatMode == REVERSE &&(iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
48 // if we were seeked to some other iteration in a reversing animator,
49 // figure out the correct direction to start playing based on the iteration
50 if (mReversing) {
51 return (iteration % 2) == 0;
52 } else {
53 return (iteration % 2) != 0;
54 }
55 } else {
56 return mReversing;
57 }
58 }
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@CallSuper
// 動畫更新計算方法
59 void animateValue(float fraction) {
60 fraction = mInterpolator.getInterpolation(fraction);
61 mCurrentFraction = fraction;
62 int numValues = mValues.length;
63 for (int i = 0; i < numValues; ++i) {
// 動畫更新計算
64 mValues[i].calculateValue(fraction);
65 }
66 if (mUpdateListeners != null) {
67 int numListeners = mUpdateListeners.size();
68 for (int i = 0; i < numListeners; ++i) {
69 mUpdateListeners.get(i).onAnimationUpdate(this);
70 }
71 }
72 }
先看一下animateBasedOnTime(long currentTime)方法第26行,再結合動畫計算方法animateValue(float fraction),可以知道,插值器getInterpolation(float fraction)方法接收引數就是第26行計算出來的currentIterationFraction ,下面我們就看看該值是如何計算的。根據原始碼,很明顯我們需要先了解第25行的mOverallFraction和fraction,這倆貨在Android屬性動畫基礎之流程解析中真的有做過說明,這裡再簡單說一下。fraction是當前時間currentTime與動畫開始時間mStartTime的差值與動畫後期的比值,不考慮邊界條件的話,其實就是動畫執行的整體時間進度(可能大於1哦,因為您可能會重複執行動畫)。那麼mOverallFraction是啥呢,它是根據fraction做邊界處理之後得到的值,也就是考慮邊界條件後的動畫整體執行時間進度,假設您設定動畫重複執行的次數為n,那麼mOverallFraction的最大值為n+1。
接下來就要看第26行了,mOverallFraction作為引數傳入getCurrentIterationFraction(float fraction)方法得到currentIterationFraction,currentIterationFraction又作引數傳入插值器getInterpolation(float input)方法,看看getCurrentIterationFraction(float fraction)方法。定位到第41行,首先根據整體執行進度計算出動畫的迭代次數(已重複執行的次數)iteration,第42行,整體進度減掉已重複執行次數得到當前執行週期內的時間進度currentFraction(其實就是週期歸一化而已,取值範圍[0,1]),如果您按照插值器getInterpolation(float input)方法api的描述來理解,那麼currentFraction就應該是input引數的接收值,然而並不一定是~,因為input引數接收的值是第43行計算出來的,沒辦法,看一下shouldPlayBackward(int iteration)方法吧。
先看第47至第54行程式碼,只解析方法內使用的引數的意義,第47行的條件判斷條件為真時,要求迭代次數即已重複執行的次數大於0;mRepeatMode(控制動畫第偶數次執行方式:倒序執行或正序執行,就像影片播放一樣,是從頭至尾還是從尾至頭)為REVERSE;已迭代次數小於等於目標重複次數。mReversing也是一個控制上述"影片"播放順序的東東,它控制的是當前動畫是否要在原來執行順序的基礎上做翻轉,該引數可通過reverse()方法更改。通過第43行程式碼及shouldPlayBackward(int iteration)方法原始碼,可以很明確的將,插值器getInterpolation(float input)方法所接收的引數值未必是當前動畫執行週期內真正的時間進度,當您需要倒序執行動畫的時候,input = 1-currentFraction = 1 * (1-currentFraction),正序執行時input = currentFraction。但是,該值的的確確是應該參與動畫更新計算的時間因子,這點並沒有問題。我們可以以影片播放來舉例說明,假設影片片長10s,共100幀,設播放的時間為t,那麼正序播放的情況下,應該播放第(int) (10 * t)幀,倒序播放的話應該播放 (int) (100 - 10 * t = 10 * (10 - t)),那麼現在你再看看,10 * (10 - t) 與上述1 * (1-currentFraction)有什麼區別?其實沒區別,非要說有區別,也就是週期是否歸一化而已,因為這裡的10就是影片週期。
綜上所述,傳入插值器getInterpolation(float input)方法中的引數值,就是原本應該參與動畫更新計算的時間因子,但是就像影片播放一樣,我們想要快放或者慢放,怎麼辦?很明顯,將原本的時間因子"篡改一下"就好了,這就是getInterpolation(float input)所做的事情,該方法根據原本真實的時間因子,計算出一個新的時間因子,然後傳入animateValue(float fraction)方法參與最終的計算(見第27行)。比影片快放慢放更強大的是,我們可以隨意"篡改",非線性的都可以。
到此為止,我們應該已經瞭解插值器的用途,下一篇文章將會介紹估值器(TypeEvaluator)
簡單示例gif如下
相關文章
- 【Android 動畫】動畫詳解之插值器(二)Android動畫
- 你是否真的瞭解全域性解析鎖(GIL)
- Android 動畫:你真的會使用插值器與估值器嗎?(含詳細例項教學)Android動畫
- 程式設計是否真的要很瞭解基礎?程式設計
- 你真的瞭解iOS怎麼取屬性的嗎?iOS
- Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解Android動畫
- 再談屬性動畫——介紹以及自定義Interpolator插值器動畫
- 前端基礎之CSS選擇器,你真的都瞭解嗎?前端CSS
- Android屬性動畫詳解(一),屬性動畫基本用法Android動畫
- 熱愛遊戲的你是否真的瞭解遊戲?遊戲
- 【Android 動畫】動畫詳解之屬性動畫(三)Android動畫
- 【Android 動畫】動畫詳解之屬性動畫(五)Android動畫
- Android 動畫詳解:屬性動畫、View 動畫和幀動畫Android動畫View
- Android 動畫之屬性動畫Android動畫
- android屬性動畫Android動畫
- Android 屬性動畫 原始碼解析 深入瞭解其內部實現Android動畫原始碼
- Android圖文詳解屬性動畫Android動畫
- 你真的瞭解Android金鑰庫嗎?Android
- android動畫——屬性動畫(Property Animation)Android動畫
- Android屬性動畫:動畫流控制Android動畫
- Android 屬性動畫(二)Android動畫
- ViewStub你真的瞭解嗎View
- 你真的瞭解mongoose嗎?Go
- 你真的瞭解 WebSocket 嗎?Web
- Android 屬性動畫實戰Android動畫
- 如何理解Android屬性動畫Android動畫
- 屬性動畫與差值器動畫
- JavaScript 你真的瞭解this指向嗎JavaScript
- 你真的瞭解前端路由嗎?前端路由
- 你真的瞭解RPC嗎?RPC
- 你真的瞭解URLEncode嗎?
- 你真的瞭解“密碼”嗎?密碼
- 你真的瞭解nosql世界嗎?SQL
- 2. Vue語法--插值操作&動態繫結屬性 詳解Vue
- Android開發之屬性動畫Android動畫
- Android 屬性動畫Property Animation(中)Android動畫
- Android 屬性動畫Property Animation(下)Android動畫
- Android學習之 屬性動畫Android動畫