屬性動畫,就是通過控制對象中的屬性值產生的動畫。屬性動畫是目前最高級的2D動畫系統。
在API Level 11中添加。Property Animation號稱能控制一切對象的動畫,包括可見的和不可見的。
但是,日常開發中我們一般都是對UI定制動畫。
使用ObjectAnimator
ObjectAnimator是其中比較容易使用的一個動畫類,它繼承自ValueAnimator,
說比較容易使用是因為它在動畫啟動后自動監視屬性值的變化並把值賦給對象屬性,
而ValueAnimator則只監視屬性值的變化,但不會自動在屬性中應用該值,因此我們需要手動應用這些值。
代碼:
//創建一個水平移動的動畫對象,從位置0到300平移 final ObjectAnimator translation = ObjectAnimator.ofFloat(tv, "translationX", 0f, 300f); translation.setDuration(1500);
只需兩行就可以創建一個簡單可運行的移動動畫。
當然還有其他setxxx()方法可以控制動畫的高級行為。
例如:添加一個插值器等
translation.setInterpolator(new AccelerateDecelerateInterpolator());
運行效果:
使用ValueAnimator
ValueAnimtor動畫的創建基本上和ObjectAnimator一樣,只是我們需要手動應用屬性值
代碼:
final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500);
有點不一樣的是在創建對象的時候,ValueAnimator無需指定屬性名稱
只需指定動畫的執行范圍。
上面我們已經說了,ValueAniamtor不會自動應用屬性值,因此我們需要添加一個動畫監聽器
translation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { tv.setTranslationX((float) animation.getAnimatedValue()); } });
tv是我們需要執行動畫的對象,這里是一個TextView對象。
要使tv對象的動畫執行,我們就要在監聽器中使用對應setxxx方法更新tv的屬性值,
而這些值我們可以使用animation對象的getAnimatedValue方法獲得。
使用動畫監聽器執行不同的任務
通過監聽器我們可以在動畫的不同狀態執行不同的任務
translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } });
例如:我們可以在前一個動畫執行完畢之后執行另一個動畫
translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { ObjectAnimator rotation = ObjectAnimator.ofFloat(tv, "rotation", 0f, 360f); rotation.setDuration(1500); rotation.start(); } });
執行效果:
使用PropertyValuesHolder控制多個對象屬性
例如我們對某個對象進行縮放控制的時候,就需要同時改變對象的x軸和y軸的值
這個時候我們就可以使用ObjectAnimator的ofPropertyValuesHolder()方法配合PropertyValuesHolder對象
//把按鈕放大1.5倍 PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f); final ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(btnStart, pvhX, pvhY); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //執行放大動畫 scale.start(); translation.start(); } });
運行效果:
使用Keyframe對象控制多屬性動畫
Keyframe對象對多屬性動畫的控制更加靈活,因為我們可以更好地控制每個時間段執行的動畫距離
Keyframe keyframe0 = Keyframe.ofFloat(0f, 0); Keyframe keyframe1 = keyframe.ofFloat(.3f, 100); Keyframe keyframe2 = keyframe.ofFloat(.4f, 200); Keyframe keyframe3 = keyframe.ofFloat(1f, 200);
PropertyValuesHolder pvhM = PropertyValuesHolder.ofKeyframe("translationX", keyframe0,keyframe1, keyframe2,keyframe3); final ObjectAnimator trans = ObjectAnimator.ofPropertyValuesHolder(tv, pvhM); trans.setDuration(1500); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //執行放大動畫 scale.start(); trans.start(); //translation.start(); } });
代碼分析:
每個keyframe變量第一個參數執行總時間0%~100%,0表示動畫未執行,1代碼動畫執行完畢
keyframe0代表動畫在0%時的位置
keyframe1代表動畫在30%時所處的位置
keyframe2代表動畫在最后一個時間段所處的位置
keyframe3代表在200個單位距離時動畫完成已經停止,加上這一行是為了動畫停止后對象不會消失
運行效果:
上面的圖片雖然不是看得很清楚,但是可以看見在最后一段時間動畫突然加快。
原因是我們在定義最后一個keyframe的時候是用10%的時間走100個單位距離,
即它要在最短的時間走最多的距離,所以它必須加快速度才能完成這個任務。
不加keyframe3時的運行效果:
完整代碼:
package com.whathecode.propertyanimation; import android.animation.Animator; import android.animation.Keyframe; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final TextView tv = (TextView) findViewById(R.id.tv); Button btnStart = (Button) findViewById(R.id.start); /* //創建一個水平移動的動畫對象,從位置0到300平移 final ObjectAnimator translation = ObjectAnimator.ofFloat(tv, "translationX", 0f, 300f); translation.setDuration(1500); */ //把按鈕放大1.5倍 PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f); final ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(btnStart, pvhX, pvhY); Keyframe keyframe0 = Keyframe.ofFloat(0f, 0);
Keyframe keyframe1 = keyframe.ofFloat(.3f, 100); Keyframe keyframe2 = keyframe.ofFloat(.4f, 200); Keyframe keyframe3 = keyframe.ofFloat(1f, 200); PropertyValuesHolder pvhM = PropertyValuesHolder.ofKeyframe("translationX", keyframe0,keyframe1, keyframe2,keyframe3); final ObjectAnimator trans = ObjectAnimator.ofPropertyValuesHolder(tv, pvhM); trans.setDuration(1500); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //執行放大動畫 scale.start(); trans.start(); //translation.start(); } }); translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { ObjectAnimator rotation = ObjectAnimator.ofFloat(tv, "rotation", 0f, 360f); rotation.setDuration(1500); rotation.start(); } }); translation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { tv.setTranslationX((float) animation.getAnimatedValue()); } }); } }