8種機械鍵盤軸體對比
本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?
前言
相對于靜態的頁面,動畫往往能更直觀地表達所需的信息,在UI開發過程中起著相當大的作用。
Android為我們提供了一系列實現動畫效果的方法,PropertyAnimaiton是最常見也是最實用的一種,如同它的名字一樣,它的實現方式是通過改變對象的一系列屬性值來改變對象的狀態, 例如動態地改變繪制的位置就可以實現繪制物體的移動效果,動態地改變對象的顯示狀態可以實現閃爍效果。
Animator概覽
Android提供的實現屬性動畫的工具是android.animation.Animator這個類,它的使用需要配合animation包下的其他工具類,這個類的功能是什么,我們要如何使用它來實現屬性動畫呢?
我們可以將Animator理解為Android為我們提供的一個按我們的需要在一定時間段內連續地計算并返回值的工具,這個值可以是通用的整型、浮點型,也可以是我們自定義的類型。
我們可以設置返回值的范圍,并可以控制值變化的快慢,例如實現自由落體下落的物體時我們需要讓高度值以一個越來越快的速度降低。
這里的連續需要注意,實際上是不可能產生真正意義上的連續值的,但是如果在繪制過程中計算這個值的速度小于繪制一幀所需要的時間,那么我們就可以在視覺上認為這個值是在連續改變的。這一點也是理解其作用的關鍵:我們很難去寫出一個可以隨時獲取連續值的工具,而Animator正是一個滿足我們這個需求的一個通用工具。
通過將Animator與View的繪制過程結合,就可以實現絕大多數的動畫效果, 但是Animator也不只局限在使用在繪制動畫,只要是有相似需求的地方都可以使用它來實現, 同時由于屬性動畫只針對屬性進行修改,與被修改對象之前幾乎沒有耦合,不需要對被修改對象作出改變,可以設置方式也多種多樣,這些都是動畫的另一種實現方法ViewAnimator所無法做到的,所以我屬性動畫是現在實現動畫效果的普遍做法。
使用Animator
Animator子類
下面就來看看如何使用Animator滿足我們的需求。
我們使用Animator可以分為兩個步驟,一是進行數值的計算,二是將計算出的數值設置到對應的對象上。而Animator有著三個子類:ValueAnimator ObjectAnimator AnimatorSet。ValueAnimator實現了上述過程的第一個步驟:進行數值的計算。第二個步驟則需要我們重寫它的回調在值發生改變時候手動地為對象更新屬性值。
ObjectAnimator則在其基礎上進行了進一步的封裝,加入了一些方法使得它可以綁定一個對象,在數值改變的同時對對象的屬性進行更新。
AnimatorSet可以對Animator進行組合,讓它們之間進行聯動,例如可以設置一個動畫根據另一個動畫的狀態來決定是否開始、暫停或停止。
可以看到,ValueAnimator提供了一個Animator最核心的內容,也是使用中最為靈活的一個。ObjectAnimator由于綁定了相應的對象,在使用上會受一些限制。AnimatorSet專用于需要組合動畫的場景。
ValueAnimator
在這篇博客中,我們關注最為核心的ValueAnimator。
關鍵屬性
ValueAnimator對象內部維護了一系列屬性來保存所需的各種信息。Duration:動畫的持續時間,通過setDuration()方法設置
Repeat count and behavior:重復計數與重復模式,我們可以通過設置這兩個屬性來控制動畫是否重復以及重復的次數,通過setRepeatCount()與setRepeatMode()方法設置
Frame refresh delay:幀刷新延遲,也就是計算兩幀動畫之間的間隔時間,但這個時間只是Animator盡力去保持的值,具體的間隔時間會由于系統負載與性能的不同而不同,同時設置它的方法為一個靜態方法:ValueAnimator.setFrameDelay(),會被設置到所有的Animator上,這是因為這些Animator都在同一個時間循環中。這個屬性也有可能會被忽略如果動畫系統采用了內部的計時來源,例如vsync來計算屬性。同時這個方法需要在與start()方法相同的進程中調用
Time interpolation:時間插值器,是我們實現不同動畫效果的關鍵,每一時刻所返回的數值由它決定,后文會詳細講
初始化與TypeEvaluator
ValueAnimator對象的構造函數只由內部使用,獲取ValueAnimator對象的方法是調用它的工廠方法:
ValueAnimator.ofArgb()
ValueAnimator.ofInt()
ValueAnimator.ofFloat()
ValueAnimator.ofObject()
ValueAnimator.ofPropertyValuesHolder() //本篇未涉及,下一篇進行講解
前三個可以看作是ValueAnimator為我們提供的初始化方式,它們的參數都是對應類型的長度可變參數:(Type ...values),我們需要提供一個以上的參數,ValueAnimator最終提供的值會在這些值之前變動。
一般情況下這里提供的Argb(用于顏色值的變化)和整型、浮點值基本可以滿足我們的需求,但是某些時候我們需要結果是我們自定義的一些對象,這個時候就需要用到TypeEvaluator<>接口了,與這個接口對應的工廠方法是ValueAnimator.ofObject():1
2ValueAnimator (TypeEvaluator evaluator,
Object... values)
這里的可變參數類型變為了Object,同時還需要我們提供一個TypeEvaluator<>,用于“告訴”Animator如何返回這個Object值。
TypeEvaluator<>接口并不復雜,只有一個方法需要我們重寫:1
2
3T evaluate (float fraction,
T startValue,
T endValue)
startValue與endValue非常好理解,就是我們在獲取Animator時指定的值的起始值和結束值。類型與返回類型一致,當然都是我們自定義的類型。
這里的fraction就是決定我們最終返回值的關鍵參數。我們可以把這個fraction理解為animator提供給我們的最終的數值改變的比例,以小數表示,小于0表示低于startValue,大于0表示超出endValue,0-1之間表示在startValue與endValue之間。我們要做的就是把這個值轉換為在起始和結果范圍之間的合適的對象值。
例如,對于基本的浮點類型,默認的FloatEvaluator是這樣的:1
2
3
4public Float evaluate(float fraction, Number startValue, Number endValue){
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
可以看到,就是相當于把fraction所表示的比例“投射”到了我們所需要的數據對象上,這里是浮點類型。如果使用我們的自定義類型,我們必須為自己的類型定義這樣的操作。
注意:這里要求我們必須將fraction線性地反應到對應的類型上,因為fraction反映的是最終的動畫進度,我們必須如實地按照這個進度改變我們的屬性,所以需要將result = x0 + t * (x1 - x0)`這樣的形式反映到我們自己的對象上。
自定義了TypeEvaluator以后就可以作為參數使用在上面的obObject()工廠方法中了。
插補細分器(Interpolators)
下面介紹使用ValueAnimator控制值變化過程中最為重要的一個概念:插補細分器(Interpolators)。
它實際上是一個關于時間的函數, 根據時刻的不同來返回不同的值,進而來控制最后的輸出的值。那么它是如何表示的呢?
系統為我們提供了一系列預置的Interpolators,以較常用的LinearInterpolater為例,顧名思義,它是一個線性的插補細分器,意味著輸入與輸出呈線性關系:1
2
3public float getInterpolation(float input){
return input;
}
輸入輸出的關鍵函數就是這個getInterpolation()了,可以看到,參數與返回值都是float類型,input的值在0-1之間,結合前面,我們可以很容易理解,這個input就是一個以0-1之間的小數表示的過去的時間值,例如整個動畫是1000ms,當input為0.25的時候意味著現在的時間過去了250ms。
而返回值就是經過我們的轉換,表示出的動畫應該進行的時間的比例,這里由于是線性的,所以可以直接返回input,這個值最后會到哪里呢?自然就是給我們前面介紹的TypeEvaluator。下面一段源碼展示了這個過程:1
2
3
4
5if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
作為getInterpolation()參數的fraction代表著過去的時間比例,這里調用我們設置的Interpolator來更新這個fraction,現在這個fraction表示的就是動畫已經進行的比例,下一步就要根據它來獲取對應的對象值(調用了我們之間談到過的evaluate()方法,這里的KeyFrame的概念會在之后的博客講到),后面的兩個參數就是傳遞給evaluate的起始與結束范圍。
最終,我們就獲得了一個按照我們設定的Interpolator返回的動畫屬性值。
如果想要實現加速效果呢?Android同樣為我們提供了現成的AccelerateInterpolator:1
2
3
4
5
6
7public float getInterpolation(float input){
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
同樣很簡潔,這里用到了mFactor與mDoubleFactor分別表示我們在構造函數里面設置的指數值:1
2
3
4public AccelerateInterpolator(float factor){
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
如果我們設置的為1,會返回input的平方,其他值則會返回input的mDoubleFactor次方,使得動畫屬性可以以不同的函數曲線形式變化。
如果我們要實現自己的Interpolator呢?只需要實現TimeInterpolator接口,這個接口只需要我們實現一個getInterpolation方法。我們可以根據input值返回不同的值來返回不同的值表示動畫的進度。
注意:返回值的范圍不一定要在0-1之間,小于0或大小1的值可以表示超出預設范圍的目標值。
這篇博客到此結束,在下一篇博客中將會以一個繪制自由落體的彈跳小球的示例來演示如何使用Animator與介紹它的回調函數。