內(nèi)容如下:
- 屬性動(dòng)畫與補(bǔ)間動(dòng)畫的不同
- 屬性動(dòng)畫工作原理
- API 概述
- 計(jì)算器 Evaluators
- 插值器 Interpolators
- 使用ValueAnimator
- 使用ObjectAnimator
- AnimatorSet
- 動(dòng)畫監(jiān)聽
- 視圖容器中布局的動(dòng)畫改變
- 使用TypeEvaluator
- 使用插值器 (Interpolators)
- 關(guān)鍵幀
- ViewPropertyAnimator
- 聲明XML
屬性動(dòng)畫與補(bǔ)間動(dòng)畫的不同
補(bǔ)間動(dòng)畫只提供了運(yùn)動(dòng)可視對(duì)象的能力陨囊,如果你想操作非可視對(duì)象弦疮,就不得不自己用代碼實(shí)現(xiàn)。實(shí)際上蜘醋,補(bǔ)間動(dòng)畫具有局限性胁塞,它只能夠使一個(gè)視圖對(duì)象的幾個(gè)方面做運(yùn)動(dòng),比如:視圖的縮放压语、旋轉(zhuǎn)啸罢,而不能操作背景顏色等等。
補(bǔ)間動(dòng)畫另一個(gè)缺點(diǎn)是:它只能夠改變視圖繪制的位置胎食,并不能改變它的真實(shí)位置扰才。例如:你在屏幕中運(yùn)動(dòng)一個(gè)按鈕,按鈕將被正確的繪制斥季,但是當(dāng)你點(diǎn)擊當(dāng)前位置的按鈕時(shí)训桶,按鈕并不會(huì)有任何改變。所以你不得不通過實(shí)現(xiàn)自己的邏輯來操作它酣倾。
對(duì)于屬性動(dòng)畫而言舵揭,這些限制被全部移除。你能夠使任何對(duì)象(可視和非可視)的任何屬性運(yùn)動(dòng)躁锡,并且對(duì)象自身被修改午绳。屬性動(dòng)畫就是通過不停修改屬性值實(shí)現(xiàn)動(dòng)畫效果的。屬性動(dòng)畫在實(shí)現(xiàn)動(dòng)畫方面更加強(qiáng)大映之。你可以根據(jù)需求將動(dòng)畫分派給對(duì)象的屬性拦焚,比如:顏色蜡坊、位置、大惺臧堋秕衙;你也可以定義動(dòng)畫的差值器以及多動(dòng)畫的同步。
不管怎樣僵刮,補(bǔ)間動(dòng)畫的創(chuàng)建可以用更少的時(shí)間和更少的代碼据忘。如果補(bǔ)間動(dòng)畫能夠完成你需要的每件事,或者代碼中已存在的補(bǔ)間動(dòng)畫代碼能夠?qū)崿F(xiàn)你的需求搞糕。你就沒必要使用屬性動(dòng)畫勇吊。通過不同的使用場(chǎng)景來確定使用兩種動(dòng)畫系統(tǒng)的哪一種。
屬性動(dòng)畫工作原理
圖1描述了一個(gè)假想的對(duì)象窍仰,沿x軸運(yùn)動(dòng)汉规,表示它在屏幕上的水平位置。在40ms內(nèi)運(yùn)動(dòng)40px驹吮,每10毫秒(默認(rèn)幀刷新率)针史,對(duì)象水平移動(dòng)10像素。到40ms時(shí)钥屈,動(dòng)畫結(jié)束悟民。對(duì)象停在40px處。這是一個(gè)線性插值動(dòng)畫的例子篷就,表示對(duì)象勻速運(yùn)動(dòng)。
還可以指定具有非線性插值的動(dòng)畫近忙。圖2假設(shè)一個(gè)對(duì)象竭业,在動(dòng)畫開始時(shí)加速,然后減速動(dòng)畫結(jié)束時(shí)及舍。該對(duì)象仍然在40ms移動(dòng)40px未辆,但非線性。
屬性動(dòng)畫系統(tǒng)的重要組件如何計(jì)算上面提到的動(dòng)畫:
ValueAnimator 對(duì)象保持跟蹤動(dòng)畫的時(shí)間锯玛,比如動(dòng)畫的已運(yùn)動(dòng)時(shí)間和動(dòng)畫的當(dāng)前值咐柜。
TimeInterpolator 定義動(dòng)畫的插值器。
TypeEvaluator 定義如何計(jì)算動(dòng)畫屬性的值攘残。
開始一個(gè)動(dòng)畫:
- 創(chuàng)建一個(gè)ValueAnimator并給它的開始屬性值拙友、結(jié)束屬性值以及動(dòng)畫的持續(xù)時(shí)間。
- 調(diào)用start()開始動(dòng)畫歼郭。
- ValueAnimator計(jì)算一個(gè)在0和1之間的逝去分?jǐn)?shù)遗契,基于動(dòng)畫的持續(xù)時(shí)間和經(jīng)過的多少時(shí)間。該分?jǐn)?shù)表示動(dòng)畫完成的時(shí)間百分比病曾,0表示0%牍蜂,1表示100%漾根。
- 當(dāng)ValueAnimator完成計(jì)算一個(gè)逝去分?jǐn)?shù),它調(diào)用當(dāng)前設(shè)置的TimeInterpolator計(jì)算插值分?jǐn)?shù)鲫竞。
- 當(dāng)插值分?jǐn)?shù)計(jì)算完成辐怕,ValueAnimator調(diào)用合適的TypeEvaluator,計(jì)算該屬性的值从绘,基于插值分?jǐn)?shù)秘蛇、起始值和動(dòng)畫結(jié)束值。
API 概述
ValueAnimator 屬性動(dòng)畫的計(jì)時(shí)引擎顶考,通過屬性值進(jìn)行動(dòng)畫處理赁还。包括計(jì)算動(dòng)畫值的核心功能,動(dòng)畫隨時(shí)間變化的細(xì)節(jié)驹沿,動(dòng)畫重復(fù)監(jiān)聽以及更新時(shí)間艘策,自定義evaluate。屬性動(dòng)畫包括兩部分:1.計(jì)算動(dòng)畫值渊季;2.對(duì)正在進(jìn)行動(dòng)畫的對(duì)象設(shè)置這些值朋蔫。ValueAnimator不能直接完成第二部分,所以必須在UpdateListener中更新屬性值却汉。
ObjectAnimator ValueAnimator的子類驯妄,可以直接設(shè)置目標(biāo)對(duì)象和屬性進(jìn)行動(dòng)畫。ObjectAnimator使用起來更方便合砂,也有限制青扔,對(duì)于進(jìn)行的動(dòng)畫的屬性必須要有setter,getter的訪問方法翩伪。
AnimatorSet 提供一種將多動(dòng)畫分組并使之關(guān)聯(lián)運(yùn)行的機(jī)制微猖。可以使動(dòng)畫同時(shí)執(zhí)行缘屹,順序執(zhí)行凛剥,延時(shí)執(zhí)行。
估算器 Evaluators
- IntEvaluator 計(jì)算int型屬性
- FloatEvaluator 計(jì)算float型屬性
- ArgbEvaluator 計(jì)算16進(jìn)制的顏色值
- TypeEvaluator 自定義計(jì)算器(更多見下文)
插值器 Interpolators
- AccelerateDecelerateInterpolator 先加速后減速
- AccelerateInterpolator 加速
- AnticipateInterpolator 向相反運(yùn)動(dòng)一小段后再開始運(yùn)行
- AnticipateOvershootInterpolator 前后都超出一小段
- BounceInterpolator 結(jié)束時(shí)振動(dòng)結(jié)束
- CycleInterpolator 往返運(yùn)行動(dòng)畫
- DecelerateInterpolator 減速運(yùn)行
- LinearInterpolator 勻速運(yùn)行
- OvershootInterpolator 結(jié)束時(shí)超出一小段
- TimeInterpolator 自定義
使用ValueAnimator
示例:使用ValueAnimator做曲線運(yùn)動(dòng)
通過調(diào)用ofInt()轻姿,ofFloat()犁珠,ofObject()方法獲得一個(gè)ValueAnimator,如下:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
還可以通過以下操作指定一個(gè)自定義類型來進(jìn)行動(dòng)畫:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue,
endPropertyValue);
animation.setDuration(1000);
animation.start();
上述代碼,當(dāng)調(diào)用start()時(shí)互亮,ValueAnimator 將在1s內(nèi)計(jì)算0-1之間的動(dòng)畫值犁享。它只是在計(jì)算一個(gè)值并沒有影響到一個(gè)具體的對(duì)象。你需要實(shí)現(xiàn)ValueAnimator.AnimatorUpdateListener這個(gè)接口胳挎,通過調(diào)用getAnimatedValue()方法可以獲得特定幀的計(jì)算值饼疙,通過這個(gè)值可以對(duì)目標(biāo)對(duì)象進(jìn)行需要的變換。
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
使用ObjectAnimator
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
擁有setter,getter方法的屬性都可以進(jìn)行動(dòng)畫操作窑眯。
經(jīng)常操作的屬性:
"alpha" 透明度屏积;
"translationY" 沿Y軸平移;
"translationX" 沿X軸平移磅甩;
"scaleX" 橫向縮放炊林;
"scaleY" 縱向縮放;
"rotation" 平面旋轉(zhuǎn)卷要;
"rotationX" 關(guān)于X軸旋轉(zhuǎn)渣聚;
"rotationY" 關(guān)于Y軸旋轉(zhuǎn);
AnimatorSet
你可以使用AnimatorSet控制多個(gè)Animator的播放順序僧叉,同時(shí)播放奕枝,順序播放,誰在前誰在后等瓶堕“溃可以嵌套使用。例:
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();
常用方法:
playSequentially (Animator...) 順序播放郎笆;
playTogether (Animator...) 一起播放谭梗;
Builder方式:
play(Animator a);
創(chuàng)建一個(gè)Builder對(duì)象;
例1:有四個(gè)動(dòng)畫anim1, amin2, anim3,anim4宛蚓。anim1和anim2同時(shí)播放;當(dāng)anim2播放結(jié)束時(shí)播放anim3;當(dāng)anim3播放結(jié)束時(shí)播放anim4激捏。
AnimatorSet s = new AnimatorSet();
s.play(anim1).with(anim2);
s.play(anim2).before(anim3);
s.play(anim4).after(anim3);
動(dòng)畫監(jiān)聽
Animator.AnimatorListener
- onAnimationStart() 動(dòng)畫開始時(shí)
- onAnimationEnd() 動(dòng)畫結(jié)束時(shí)
- onAnimationRepeat() 動(dòng)畫重復(fù)時(shí)
- onAnimationCancel() 動(dòng)畫取消時(shí),同時(shí)調(diào)用onAnimationEnd()
ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate() 每幀動(dòng)畫都將被調(diào)用
通過getAnimatedValue()方法獲取動(dòng)畫值凄吏。
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());
}
)};
視圖容器中布局的動(dòng)畫改變
使用TypeEvaluator
如果你想以系統(tǒng)未知的方式運(yùn)動(dòng)远舅,你可以通過實(shí)現(xiàn)TypeEvaluator接口來創(chuàng)建。實(shí)現(xiàn)TypeEvaluator接口只需要實(shí)現(xiàn)一個(gè)方法evaluate()竞思。如下:
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);
}
}
例:使TextView沿正弦曲線運(yùn)動(dòng)
創(chuàng)建包裝類:
private class TextHolder
private TextView view;
public TextHolder(TextView textView){
view=textView;
}
public XYHolder getXY() {
return new XYHolder(view.getX(),view.getY());
}
public void setXY(XYHolder xyHolder) {
view.setX(xyHolder.getX());
view.setY(xyHolder.getY());
}
}
估值器:
public class SineTypeEvaluator implements TypeEvaluator<XYHolder> {
@Override
public XYHolder evaluate(float fraction, XYHolder startValue, XYHolder endValue) {
float xHolder=startValue.getX()+fraction*(endValue.getX()-startValue.getX());
float yHolder=(float) (300*Math.sin(xHolder/100)+endValue.getY());
return new XYHolder(xHolder,yHolder );
}
}
開始動(dòng)畫:
ObjectAnimator objectAnimator=ObjectAnimator.ofObject(new TextHolder(mText1),"xY",new
SineTypeEvaluator(),new XYHolder(0f,400f),new XYHolder(628f,400f));
objectAnimator.setDuration(2000);
objectAnimator.start();
使用插值器
一個(gè)隨時(shí)間計(jì)算動(dòng)畫值的函數(shù)
AccelerateDecelerateInterpolator
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
LinearInterpolator
public float getInterpolation(float input) {
return input;
}
關(guān)鍵幀
例如:
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提供一種更簡單的多屬性運(yùn)動(dòng)的方式表谊。如下對(duì)比:
使用多個(gè)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();
使用一個(gè)ObjectAnimator:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
使用ViewPropertyAnimator:
myView.animate().x(50f).y(100f);</pre>
聲明XML
將xml文件保存在res/animator/ 目錄下
動(dòng)畫類與xml標(biāo)簽的對(duì)應(yīng)關(guān)系:
ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet- <set>
下面的示例依次播放兩組對(duì)象動(dòng)畫,第一個(gè)動(dòng)畫集合同時(shí)播放兩個(gè)對(duì)象動(dòng)畫:
<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>
運(yùn)行上述動(dòng)畫:
AnimatorSet set = (AnimatorSet)
AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
set.setTarget(myObject);
set.start();
在XML中聲明ValueAnimator:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueType="floatType"
android:valueFrom="0f"
android:valueTo="-100f" />
ValueAnimator必須實(shí)現(xiàn)AnimatorUpdateListener盖喷,如下:
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();