屬性動畫可以在以下幾個方面對動畫進行設置:
- Duration:動畫持續時間,默認是300ms。
- Time interpolation:插值器,可以指定屬性值如何根據動畫當前播放時間進行變換。
- Repeat count and behavior:重複次數和重複方式,正向或者逆向重複。
- Animator sets:動畫集合,可以對動畫進行分類組合,指定播放順序或者播放延時。
- Frame refresh delay:可以指定多久更新一次動畫的幀,默認是10ms更新一次,但是應用更新動畫的速度很大程度上取決於系統的繁忙程度,以及系統可以多快響應內含的定時器。
工作原理
ValueAnimator這個類記錄着動畫的計時,還封裝了TimeInterpolator和TypeEvaluator兩個類。TimeInterpolator用來計算時間到動畫執行比例的變換,TypeEvaluator用來計算變化的屬性值。
當ValueAnimator調用start()之後,ValueAnimator會根據動畫時長和已流逝時間來計算時間流逝百分比。計算完時間流逝百分比後,ValueAnimator就會調用當前的TimeInterpolator來計算插值調整過後的實際百分比,然後根據實際百分比調用TypeEvaluator來計算當前的屬性值。
ValueAnimator的使用
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
// 自定義類型求值器
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
// 監聽值的變化
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
ObjectAnimator的使用
ObjectAnimator是ValueAnimator的子類,幫我們把ValueAnimator改變後的值設置到對象上面,省去設置監聽的步驟。
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
如果要讓ObjectAnimator可以正常更新屬性值,必須要滿足以下幾個條件:
- 所要設置動畫的對象屬性必須有對應的setter方法,如果沒有的話,可以採取三種方案之一:1. 有權限修改這個類的話直接加一個setter方法 2. 使用一個包裝類,包裝類包含setter,然後再把設置的值轉給目標對象屬性。3. 使用ValueAnimator。
- 如果ObjectAnimator的工廠方法中values…可變參數的個數是一個,那這個參數值默認是動畫的結束值,因此該對象屬性必須有一個對應的getter方法來獲取動畫的初始值。
- 如果有getter,和setter操作的屬性類型必須是一樣的。
- 根據執行動畫的特定屬性或者對象,可能需要調用invalidate()來強制重繪,可以在onAnimationUpdate()回調方法中操作。
使用動畫集合播放多個動畫
可以使用AnimatorSet來控制多個動畫的播放,AnimatorSet可以自己嵌套自己。
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
動畫監聽
- Animator.AnimatorListener
- onAnimationStart():動畫開始時調用
- onAnimationEnd():動畫結束時調用
- onAnimationRepeat():動畫重複時調用
- onAnimationCancel():動畫取消時調用,動畫取消時也會調用onAnimationEnd()
- ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate():動畫的每一幀都會調用這個回調,可以調用ValueAnimator對象的getAnimatedValue()來獲取當前值。
如果不想要實現AnimatorListener中的所有回調,可以繼承AnimatorListenerAdapter來選擇自己需要的回調去重寫。
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
balls.remove(((ObjectAnimator)animation).getTarget());
}
ViewGroup的佈局動畫
屬性動畫在給View對象提供動畫的同時,也可以用來給ViewGroup的佈局變化設置動畫。
佈局動畫是通過LayoutTransition類來實現。佈局動畫指的是ViewGroup當中View的添加或刪除,或者可見性的改變,對相應的View進行動畫出現或者消失。其他View也可以動畫移動到新的位置上。
通過調用LayoutTransition對象的setAnimator()方法來設置動畫,可以選擇以下的常量來設置:
- APPEARING:表示這個動畫用來作用在正在顯示的View。
- CHANGE_APPEARING :表示這個動畫用來作用在因爲其他新顯示View而改變位置的View上。
- DISAPPEARING :表示這個動畫用來作用在正在消失的View。
- CHANGE_DISAPPEARING :表示這個動畫用來作用在因爲其他新消失View而改變位置的View上。
如果要使用系統默認的佈局動畫,只要加上animateLaoutChanges=true即可:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />
使用TypeEvaluator
系統已經實現的TypeEvaluator包括IntEvaluator、FloatEvaluator、ArgbEvaluator,如果要對自定義類型進行動畫,那就要實現TypeEvaluator接口:
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);
}
}
使用插值器
插值器是用來計算屬性值如何隨着動畫流逝比例進行變化的。系統提供的插值器在android.view.animation包中,如果要自定義插值器可以實現TimeInterpolator接口。
兩種系統插值器的實現:
LinearInterpolator:
public float getInterpolation(float input) {
return input;
}
AccelerateDecelerateInterpolator:
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
指定關鍵幀
一個Keyframe對象用來指定某個時間點的具體屬性值,兩個Keyframe之間還可以指定interpolator來控制中間的變化行爲。
具體使用就是先調用工廠方法獲取Keyframe對象,然後調用PropertyValuesHolder的靜態方法ofKeyframe()獲取PropertyValuesHolder對象,再調用ObjectAnimator的靜態ofPropertyValuesHolder()獲取ObjectAnimator。
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);
使用ViewPropertyAnimator
ViewPropertyAnimator用來對View的多個屬性同時進行動畫。組合多個ObjectAnimator也可以進行多屬性動畫,不過會比較麻煩。
1. 單純ObjectAnimator進行多屬性動畫
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
2. 配合ViewPropertyAnimator進行多屬性動畫
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
另一種更簡潔的用法:
myView.animate().x(50f).y(100f);
ObjectAnimator
ObjectAnimator有個特殊的rotateX和rotateY屬性,是在三維維度上進行動畫的。
在XML中聲明動畫
爲了跟補間動畫區分開來,屬性動畫的XML文件目錄是res/animator/。
以下爲類及其對應的XML標籤:
- ValueAnimator :
<animator>
- ObjectAnimator :
<objectAnimator>
- AnimatorSet :
<set>
XML示例:
<set android:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="intType"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>
</set>
XML動畫的使用:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
ValueAnimator示例:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueType="floatType"
android:valueFrom="0f"
android:valueTo="-100f" />
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
xmlAnimator.start();
動畫對UI性能的影響
更新UI的動畫會對UI每一幀的渲染增加額外的工作,是在渲染管道的動畫階段進行的,可以通過啓用Profile GPU Rendering來查看動畫階段的性能表現。