導論
本文着重講解Android3.0後推出的屬性動畫框架Property Animation——Animator。
產生原因
3.0之前已有的動畫框架——Animation存在一些侷限性, Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然後調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啓動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能相應事件。
而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator進行更精細化控制,只控制一個對象的一個屬性值,多個ObjectAnimator組合到AnimatorSet形成一個動畫。而且ObjectAnimator能夠自動驅動,可以調用setFrameDelay(longframeDelay)設置動畫幀之間的間隙時間,調整幀率,減少動畫過程中頻繁繪製界面,而在不影響動畫效果的前提下減少CPU資源消耗。因此,Anroid推出的強大的屬性動畫框架,基本可以實現所有的動畫效果。
強大的原因
因爲屬性動畫框架操作的是真實的屬性值,直接變化了對象的屬性,因此可以很靈活的實現各種效果,而不侷限於以前的4種動畫效
ObjectAnimator
ObjectAnimator是屬性動畫框架中最重要的實行類,創建一個ObjectAnimator只需通過他的靜態工廠類直接返回一個ObjectAnimator對象。傳的參數包括一個對象和對象的屬性名字,但這個屬性必須有get和set函數,內部會通過java反射機制來調用set函數修改對象屬性值。還包括屬性的初始值,最終值,還可以調用setInterpolator設置曲線函數。
ObjectAnimator實例
1
2
3
4
|
ObjectAnimator .ofFloat(view,
"rotationX" ,
0 .0F,
360 .0F) .setDuration( 1000 ) .start(); |
這個例子很簡單,針對view的屬性rotationX進行持續時間爲1000ms的0到360的角度變換。
PS:可操縱的屬性參數:x/y;scaleX/scaleY;rotationX/ rotationY;transitionX/ transitionY等等。
PS:X是View最終的位置、translationX爲最終位置與佈局時初始位置的差。所以若就用translationX即爲在原來基礎上移動多少,X爲最終多少。getX()的值爲getLeft()與getTranslationX()的和。
動畫繪製過程的監聽
1
2
3
4
5
|
animator.addUpdateListener( new
AnimatorUpdateListener() { @Override public
void
onAnimationUpdate(ValueAnimator arg0) { } }); |
該方法用來監聽動畫繪製過程中的每一幀的改變,通過這個方法,我們可以在動畫重繪的過程中,實現自己的邏輯。
同時修改多個屬性值
當然這個可以使用Animationset來實現,這裏我們使用一種取巧的方法來實現:
1
2
3
4
5
6
7
8
9
10
11
12
|
ObjectAnimator
anim = ObjectAnimator.ofFloat(view, "xxx" ,
1 .0F,
0 .0F) .setDuration( 500 ); anim.start(); anim.addUpdateListener( new
AnimatorUpdateListener() { @Override public
void
onAnimationUpdate(ValueAnimator animation) { floatcVal
= (Float) animation.getAnimatedValue(); view.setAlpha(cVal); view.setScaleX(cVal); view.setScaleY(cVal); } }); |
我們可以監聽一個並不存在的屬性,而在監聽動畫更新的方法中,去修改view的屬性,監聽一個不存在的屬性的原因就是,我們只需要動畫的變化值,通過這個值,我們自己來實現要修改的效果,實際上,更直接的方法,就是使用ValueAnimator來實現,其實ObjectAnimator就是ValueAnimator的子類,這個在下面會具體講到。
爲不具有get/set方法的屬性提供修改方法
Google在應用層爲我們提供了2種解決方法,一種是通過自己寫一個包裝類,來爲該屬性提供get/set方法,還有一種是通過ValueAnimator來實現,ValueAnimator的方法我們在下面會具體講解,這裏講解下如何使用自定義的包裝類來給屬性提供get/set方法。
包裝類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private
static
class
WrapperView { private
View mTarget; public
WrapperView(View target) { mTarget
= target; } public
int
getWidth() { return
mTarget.getLayoutParams().width; } public
void
setWidth( int
width) { mTarget.getLayoutParams().width
= width; mTarget.requestLayout(); } } |
使用方法:
1
2
|
ViewWrapper
wrapper = new
ViewWrapper(mButton); ObjectAnimator.ofInt(wrapper,
"width" ,
500 ).setDuration( 5000 ).start(); |
這樣就間接給他加上了get/set方法,從而可以修改其屬性實現動畫效果。
多動畫效果的另一種實現方法——propertyValuesHolder
1
2
3
4
5
6
7
8
9
10
|
public
void
propertyValuesHolder(View view) { PropertyValuesHolder
pvhX = PropertyValuesHolder.ofFloat( "alpha" ,
1f, 0f,
1f); PropertyValuesHolder
pvhY = PropertyValuesHolder.ofFloat( "scaleX" ,
1f, 0 ,
1f); PropertyValuesHolder
pvhZ = PropertyValuesHolder.ofFloat( "scaleY" ,
1f, 0 ,
1f); ObjectAnimator.ofPropertyValuesHolder(view,
pvhX, pvhY, pvhZ) .setDuration( 1000 ).start(); } |
ValueAnimator
說簡單點,ValueAnimator就是一個數值產生器,他本身不作用於任何一個對象,但是可以對產生的值進行動畫處理。
1
2
3
4
5
6
7
8
9
10
|
ValueAnimator
animator = ValueAnimator.ofFloat( 0 ,
100 ); animator.setTarget(view); animator.setDuration( 1000 ).start(); animator.addUpdateListener( new
AnimatorUpdateListener() { @Override public
void
onAnimationUpdate(ValueAnimator animation) { Float
value = (Float) animation.getAnimatedValue(); imageView.setTranslationY(value); } }); |
通過這個動畫我們可以發現,和我們在上面提供的使用ObjectAnimator的方法很像,的確,我前面說這個纔是專業的寫法,就是這個原因,動畫生成的原理就是通過差值器計算出來的一定規律變化的數值作用到對象上來實現對象效果的變化,因此我們可以使用ObjectAnimator來生成這些數,然後在動畫重繪的監聽中,完成自己的效果。
ValueAnimator是計算動畫過程中變化的值,包含動畫的開始值,結束值,持續時間等屬性。但並沒有把這些計算出來的值應用到具體的對象上面,所以也不會有什麼的動畫顯示出來。要把計算出來的值應用到對象上,必須爲ValueAnimator註冊一個監聽器ValueAnimator.AnimatorUpdateListener,該監聽器負責更新對象的屬性值。在實現這個監聽器的時候,可以通過getAnimatedValue()的方法來獲取當前幀的值。
ValueAnimator封裝了一個TimeInterpolator,TimeInterpolator定義了屬性值在開始值與結束值之間的插值方法。ValueAnimator還封裝了一個TypeAnimator,根據開始、結束值與TimeIniterpolator計算得到的值計算出屬性值。ValueAnimator根據動畫已進行的時間跟動畫總時間(duration)的比計算出一個時間因子(0~1),然後根據TimeInterpolator計算出另一個因子,最後TypeAnimator通過這個因子計算出屬性值,例如在10ms時(total 40ms):
首先計算出時間因子,即經過的時間百分比:t=10ms/40ms=0.25
經插值計算(inteplator)後的插值因子:大約爲0.15,如果使用了AccelerateDecelerateInterpolator,計算公式爲(input即爲時間因子):
(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
最後根據TypeEvaluator計算出在10ms時的屬性值:0.15*(40-0)=6pixel。如果使用TypeEvaluator爲FloatEvaluator,計算方法爲 :
1
2
3
4
|
public
Float evaluate( float
fraction, Number startValue, Number endValue) { float
startFloat = startValue.floatValue(); return
startFloat + fraction * (endValue.floatValue() - startFloat); } |
參數分別爲上一步的插值因子,開始值與結束值。
ValueAnimator與ObjectAnimator實例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
package
com.example.animtest; import
android.animation.TypeEvaluator; import
android.animation.ValueAnimator; import
android.animation.ValueAnimator.AnimatorUpdateListener; import
android.app.Activity; import
android.graphics.PointF; import
android.os.Bundle; import
android.util.DisplayMetrics; import
android.view.View; import
android.view.Window; import
android.view.WindowManager; import
android.view.animation.BounceInterpolator; import
android.view.animation.LinearInterpolator; import
android.widget.ImageView; public
class
AnimateFreeFall extends
Activity { private
int
screenHeight; private
int
screenWidth; private
ImageView imageView; @Override protected
void
onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.animate_free_fall); DisplayMetrics
metrics = new
DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); screenHeight
= metrics.heightPixels; screenWidth
= metrics.widthPixels; imageView
= (ImageView) findViewById(R.id.im); } public
void
clean(View view) { imageView.setTranslationX( 0 ); imageView.setTranslationY( 0 ); } public
void
freefall(View view) { final
ValueAnimator animator = ValueAnimator.ofFloat( 0 ,
screenHeight -
imageView.getHeight()); animator.setTarget(view); animator.setInterpolator( new
BounceInterpolator()); animator.setDuration( 1000 ).start(); animator.addUpdateListener( new
AnimatorUpdateListener() { @Override public
void
onAnimationUpdate(ValueAnimator animation) { Float
value = (Float) animation.getAnimatedValue(); imageView.setTranslationY(value); } }); } public
void
parabola(View view) { ValueAnimator
animator = ValueAnimator.ofObject( new
TypeEvaluator<PointF>() { @Override public
PointF evaluate( float
fraction, PointF arg1, PointF
arg2) { PointF
p = new
PointF(); p.x
= fraction * screenWidth; p.y
= fraction * fraction * 0 .5f
* screenHeight * 4f *
0 .5f; return
p; } },
new
PointF( 0 ,
0 )); animator.setDuration( 800 ); animator.setInterpolator( new
LinearInterpolator()); animator.start(); animator.addUpdateListener( new
AnimatorUpdateListener() { @Override public
void
onAnimationUpdate(ValueAnimator animator) { PointF
p = (PointF) animator.getAnimatedValue(); imageView.setTranslationX(p.x); imageView.setTranslationY(p.y); } }); } } |
效果如下圖:
有一點需要注意的是,由於ofInt,ofFloat等無法使用,我們自定義了一個TypeValue,每次根據當前時間返回一個PointF對象,(PointF和Point的區別就是x,y的單位一個是float,一個是int point float的意思)PointF中包含了x,y的當前位置,然後在更新監聽中更新。
自定義TypeEvaluator傳入的泛型可以根據自己的需求,自己設計個Bean。
動畫事件的監聽
通過監聽這個事件在屬性的值更新時執行相應的操作,對於ValueAnimator一般要監聽此事件執行相應的動作,不然Animation沒意義(但是可用於計時),在ObjectAnimator(繼承自ValueAnimator)中會自動更新屬性,所以不必監聽。在函數中會傳遞一個ValueAnimator參數,通過此參數的getAnimatedValue()取得當前動畫屬性值。
PS:根據應用動畫的對象或屬性的不同,可能需要在onAnimationUpdate函數中調用invalidate()函數刷新視圖通過動畫的各種狀態,我們可以監聽動畫的各種狀態。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
ObjectAnimator
anim = ObjectAnimator.ofFloat(view, "alpha" ,
0 .5f); anim.addListener( new
AnimatorListener() { @Override public
void
onAnimationStart(Animator animation) { } @Override public
void
onAnimationRepeat(Animator animation) { } @Override public
void
onAnimationEnd(Animator animation) { } @Override public
void
onAnimationCancel(Animator animation) { } }); anim.start(); |
可以看見,API提供了開始、重複、結束、取消等各種狀態的監聽,同時,API還提供了一種簡單的監聽方法,可以不用監聽所有的事件:
1
2
3
4
5
|
anim.addListener( new
AnimatorListenerAdapter() { @Override public
void
onAnimationEnd(Animator animation) { } }); |
通過AnimatorListenerAdapter來選擇你需要監聽的事件
動畫監聽的實例應用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package
com.example.animtest; import
android.animation.Animator; import
android.animation.AnimatorListenerAdapter; import
android.animation.ObjectAnimator; import
android.app.Activity; import
android.os.Bundle; import
android.view.View; import
android.widget.ImageView; public
class
AnimateMoveInSecond extends
Activity { private
ImageView imageView; @Override protected
void
onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.animate_move_in_second); imageView
= (ImageView) findViewById(R.id.imageView1); } public
void
doit(View view) { ObjectAnimator
animator = ObjectAnimator.ofFloat(imageView, "alpha" , 1 .0f,
0f); animator.setDuration( 1000 ); animator.start(); animator.addListener( new
AnimatorListenerAdapter() { @Override public
void
onAnimationEnd(Animator animation) { super .onAnimationEnd(animation); ObjectAnimator
animator = ObjectAnimator.ofFloat(imageView, "alpha" ,
0f, 1 .0f); animator.setDuration( 1000 ); animator.start(); imageView.setTranslationY( 400 ); } }); } } |
效果如下圖:
AnimatorSet
AnimatorSet用於實現多個動畫的協同作用。效果如下:
1
2
3
4
5
6
7
8
9
10
|
ObjectAnimator
animator1 = ObjectAnimator.ofFloat(imageView, "scaleX" , 1f,
2f); ObjectAnimator
animator2 = ObjectAnimator.ofFloat(imageView, "scaleY" , 1f,
2f); ObjectAnimator
animator3 = ObjectAnimator.ofFloat(imageView, "translationY" ,
0f, 500f); AnimatorSet
set = new
AnimatorSet(); set.setDuration( 1000 ); set.playTogether(animator1,
animator2, animator3); set.start(); |
AnimatorSet中有一系列的順序控制方法:playTogether、playSequentially、animSet.play().with()、defore()、after()等。用來實現多個動畫的協同工作方式。
使用xml來創建動畫
屬性動畫於以前的動畫一樣,也支持通過xml文件來創建動畫,下面是一個簡單的例子:
1
2
3
4
5
6
7
8
|
<?xml
version= "1.0"
encoding= "utf-8" ?> <objectAnimator
xmlns:android= "http://schemas.android.com/apk/res/android" android:duration= "1000" android:propertyName= "scaleX" android:valueFrom= "1.0" android:valueTo= "2.0" android:valueType= "floatType"
> </objectAnimator> |
1
2
3
4
5
6
7
|
public
void
scaleX(View view) { //
加載動畫 Animator
anim = AnimatorInflater.loadAnimator( this ,
R.animator.scalex); anim.setTarget(mMv); anim.start(); } |
佈局動畫
佈局動畫是指ViewGroup在佈局時產生的動畫效果
LayoutTransition動畫
通過LayoutTransition來實現容器在添加子view的時候的動畫過渡效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package
com.example.animtest; import
android.animation.Animator; import
android.animation.AnimatorListenerAdapter; import
android.animation.Keyframe; import
android.animation.LayoutTransition; import
android.animation.ObjectAnimator; import
android.animation.PropertyValuesHolder; import
android.app.Activity; import
android.os.Bundle; import
android.view.View; import
android.view.View.OnClickListener; import
android.widget.Button; import
android.widget.LinearLayout; public
class
AnimateLayoutTransition extends
Activity { private
LinearLayout ll; private
LayoutTransition mTransition = new
LayoutTransition(); @Override protected
void
onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.animate_layout_transition); ll
= (LinearLayout) findViewById(R.id.ll); setupCustomAnimations(); ll.setLayoutTransition(mTransition); } public
void
add(View view) { final
Button button = new
Button( this ); ll.addView(button); button.setOnClickListener( new
OnClickListener() { @Override public
void
onClick(View arg0) { ll.removeView(button); } }); } //
生成自定義動畫 private
void
setupCustomAnimations() { //
動畫:CHANGE_APPEARING //
Changing while Adding PropertyValuesHolder
pvhLeft = PropertyValuesHolder.ofInt( "left" ,
0 ,
1 ); PropertyValuesHolder
pvhTop = PropertyValuesHolder.ofInt( "top" ,
0 ,
1 ); PropertyValuesHolder
pvhRight = PropertyValuesHolder.ofInt( "right" ,
0 , 1 ); PropertyValuesHolder
pvhBottom = PropertyValuesHolder.ofInt( "bottom" , 0 ,
1 ); PropertyValuesHolder
pvhScaleX = PropertyValuesHolder.ofFloat( "scaleX" , 1f,
0f, 1f); PropertyValuesHolder
pvhScaleY = PropertyValuesHolder.ofFloat( "scaleY" , 1f,
0f, 1f); final
ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder( this ,
pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX, pvhScaleY).setDuration( mTransition.getDuration(LayoutTransition.CHANGE_APPEARING)); mTransition.setAnimator(LayoutTransition.CHANGE_APPEARING,
changeIn); changeIn.addListener( new
AnimatorListenerAdapter() { public
void
onAnimationEnd(Animator anim) { View
view = (View) ((ObjectAnimator) anim).getTarget(); //
View也支持此種動畫執行方式了 view.setScaleX(1f); view.setScaleY(1f); } }); //
動畫:CHANGE_DISAPPEARING //
Changing while Removing Keyframe
kf0 = Keyframe.ofFloat(0f, 0f); Keyframe
kf1 = Keyframe.ofFloat(.9999f, 360f); Keyframe
kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder
pvhRotation = PropertyValuesHolder.ofKeyframe( "rotation" ,
kf0, kf1, kf2); final
ObjectAnimator changeOut = ObjectAnimator .ofPropertyValuesHolder( this ,
pvhLeft, pvhTop, pvhRight, pvhBottom,
pvhRotation) .setDuration( mTransition .getDuration(LayoutTransition.CHANGE_DISAPPEARING)); mTransition .setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
changeOut); changeOut.addListener( new
AnimatorListenerAdapter() { public
void
onAnimationEnd(Animator anim) { View
view = (View) ((ObjectAnimator) anim).getTarget(); view.setRotation(0f); } }); //
動畫:APPEARING //
Adding ObjectAnimator
animIn = ObjectAnimator.ofFloat( null ,
"rotationY" ,
90f, 0f).setDuration( mTransition.getDuration(LayoutTransition.APPEARING)); mTransition.setAnimator(LayoutTransition.APPEARING,
animIn); animIn.addListener( new
AnimatorListenerAdapter() { public
void
onAnimationEnd(Animator anim) { View
view = (View) ((ObjectAnimator) anim).getTarget(); view.setRotationY(0f); } }); //
動畫:DISAPPEARING //
Removing ObjectAnimator
animOut = ObjectAnimator.ofFloat( null ,
"rotationX" ,
0f, 90f).setDuration( mTransition.getDuration(LayoutTransition.DISAPPEARING)); mTransition.setAnimator(LayoutTransition.DISAPPEARING,
animOut); animOut.addListener( new
AnimatorListenerAdapter() { public
void
onAnimationEnd(Animator anim) { View
view = (View) ((ObjectAnimator) anim).getTarget(); view.setRotationX(0f); } }); } } |
上面的例子中自定義了 LayoutTransition來修改默認的過渡動畫,如果保持默認則使用系統默認的動畫效果。
過渡的類型一共有四種:
LayoutTransition.APPEARING 當一個View在ViewGroup中出現時,對此View設置的動畫
LayoutTransition.CHANGE_APPEARING當一個View在ViewGroup中出現時,對此View對其他View位置造成影響,對其他View設置的動畫
LayoutTransition.DISAPPEARING當一個View在ViewGroup中消失時,對此View設置的動畫
LayoutTransition.CHANGE_DISAPPEARING當一個View在ViewGroup中消失時,對此View對其他View位置造成影響,對其他View設置的動畫
LayoutTransition.CHANGE 不是由於View出現或消失造成對其他View位置造成影響,然後對其他View設置的動畫。
注意動畫到底設置在誰身上,此View還是其他View。
AnimateLayoutChanges動畫
ViewGroup的xml屬性中有一個默認的animateLayoutChanges屬性,設置該屬性,可以添加ViewGroup增加view的過渡效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<? xml
version = "1.0"
encoding = "utf-8" ?> android:id = "@+id/ll" android:layout_width = "match_parent" android:layout_height = "match_parent" android:animateLayoutChanges = "true" android:orientation = "vertical"
> < Button android:id = "@+id/button1" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:onClick = "add" android:text = "Add
Button"
/> </ LinearLayout > |
LayoutAnimation動畫
通過設置LayoutAnimation也同樣可以實現佈局動畫效果,實例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package
com.example.animtest; import
android.app.Activity; import
android.os.Bundle; import
android.view.animation.LayoutAnimationController; import
android.view.animation.ScaleAnimation; import
android.widget.LinearLayout; public
class
AnimateLayoutAnimation extends
Activity { @Override protected
void
onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.animate_layout_animation); LinearLayout
ll = (LinearLayout) findViewById(R.id.ll); ScaleAnimation
sa = new
ScaleAnimation( 0 ,
1 ,
0 ,
1 ); sa.setDuration( 2000 ); //
第二個參數dely : the delay by which each child's animation must be offset LayoutAnimationController
lac = new
LayoutAnimationController(sa, 0 .5F); //
設置顯示的順序 這個必須要在dely不爲0的時候纔有效 lac.setOrder(LayoutAnimationController.ORDER_NORMAL); ll.setLayoutAnimation(lac); } } |
View的animate方法
3.0後android對View也提供了直接作用的動畫API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
view.animate().alpha( 0 ).y( 100 ).setDuration( 1000 ) .withStartAction( new
Runnable() { @Override public
void
run() { } }).withEndAction( new
Runnable() { @Override public
void
run() { runOnUiThread( new
Runnable() { @Override public
void
run() { } }); } }).start(); |
Interpolators(插值器)
插值器和估值器,是實現非線性動畫的基礎,瞭解這些東西,才能作出不一樣的動畫效果。所謂的插值器,就是通過一些數學物理公式,計算出一些數值,提供給動畫來使用。就好比我們定義了起始值是0,結束值是100,但是這0到100具體是怎麼變化的呢,這就是插值器產生的結果,線性的,就是勻速增長,加速的,就是按加速度增長。這些增加的算法公式,已經不需要我們來自己設計了,Android內置了7種插值器,基本可以滿足需求,當然你也可以自定新的插值器。
AccelerateInterpolator 加速
Decelerate 減速
AccelerateDecelerateInterpolator 開始,和結尾都很慢,但是,中間加速
AnticipateInterpolator 開始向後一點,然後,往前拋
OvershootInterpolator 往前拋超過一點,然後返回來
AnticipateOvershootInterpolator 開始向後一點,往前拋過點,然後返回來
BounceInterpolator 結束的時候彈一下
LinearInterpolator 默認 勻速
TypeEvalutors (估值器)
根據屬性的開始、結束值與TimeInterpolation計算出的因子計算出當前時間的屬性值,android提供了以下幾個evalutor:
IntEvaluator:屬性的值類型爲int;
FloatEvaluator:屬性的值類型爲float;
ArgbEvaluator:屬性的值類型爲十六進制顏色值;
TypeEvaluator:一個接口,可以通過實現該接口自定義Evaluator。
自定義TypeEvalutor很簡單,只需要實現一個方法,如FloatEvalutor的定義:
1
2
3
4
5
6
7
8
|
public
class
FloatEvaluator implements
TypeEvaluator { public
Object evaluate( float
fraction, Object startValue, Object
endValue) { float
startFloat = ((Number) startValue).floatValue(); return
startFloat + fraction *
(((Number) endValue).floatValue() - startFloat); } } |
根據動畫執行的時間跟應用的Interplator,會計算出一個0~1之間的因子,即evalute函數中的fraction參數。
KeyFrame
keyFrame是一個 時間/值 對,通過它可以定義一個在特定時間的特定狀態,即關鍵幀,而且在兩個keyFrame之間可以定義不同的Interpolator,就好像多個動畫的拼接,第一個動畫的結束點是第二個動畫的開始點。KeyFrame是抽象類,要通過ofInt(),ofFloat(),ofObject()獲得適當的KeyFrame,然後通過PropertyValuesHolder.ofKeyframe獲得PropertyValuesHolder對象,如以下例子:
1
2
3
4
5
6
7
8
|
Keyframe
kf0 = Keyframe.ofInt( 0 ,
400 ); Keyframe
kf1 = Keyframe.ofInt( 0 .25f,
200 ); Keyframe
kf2 = Keyframe.ofInt( 0 .5f,
400 ); Keyframe
kf4 = Keyframe.ofInt( 0 .75f,
100 ); Keyframe
kf3 = Keyframe.ofInt(1f, 500 ); PropertyValuesHolder
pvhRotation = PropertyValuesHolder.ofKeyframe( "width" ,
kf0, kf1, kf2, kf4, kf3); ObjectAnimator
rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation); rotationAnim.setDuration( 2000 ); |
上述代碼的意思爲:設置btn對象的width屬性值使其:
開始時 Width=400
動畫開始1/4時 Width=200
動畫開始1/2時 Width=400
動畫開始3/4時 Width=100
動畫結束時 Width=500
第一個參數爲時間百分比,第二個參數是在第一個參數的時間時的屬性值。
定義了一些Keyframe後,通過PropertyValuesHolder類的方法ofKeyframe一個PropertyValuesHolder對象,然後通過ObjectAnimator.ofPropertyValuesHolder獲得一個Animator對象。
用下面的代碼可以實現同樣的效果(上述代碼時間值是線性,變化均勻):
1
2
3
|
ObjectAnimator
oa=ObjectAnimator.ofInt(btn2, "width" ,
400 , 200 , 400 , 100 , 500 ); oa.setDuration( 2000 ); oa.start(); |