注意:本篇文章是本人閱讀關(guān)于Android動(dòng)畫的文章所寫下的總結(jié)徘意,方便以后查閱,所有內(nèi)容非原創(chuàng),侵權(quán)刪宝穗。
本篇文章內(nèi)容來自于
- Android高級(jí)進(jìn)階 顧浩鑫
- Android自定義控件三部曲文章索引之動(dòng)畫篇
目錄
4.屬性動(dòng)畫PropertyAnimation(基類Animator)
--4.1 ValueAnimator
----4.1.1 ValueAnimator構(gòu)造函數(shù)
----4.1.2 ValueAnimator常用方法
----4.1.3 ValueAnimator監(jiān)聽器(2種)
----4.1.4 ValueAnimator代碼實(shí)現(xiàn)(5種)【3和5待補(bǔ)】
----4.1.5 首先理解interpolator和Evaluator的功能和關(guān)系
----4.1.6 interpolator插值器(系統(tǒng)+自定義)
----4.1.7 Evaluator(系統(tǒng)+自定義)
----4.1.8 ValueAnimator XML實(shí)現(xiàn)
4.屬性動(dòng)畫PropertyAnimation(基類Animator)
一個(gè)完整的屬性動(dòng)畫由兩部分組成:
1.計(jì)算動(dòng)畫各個(gè)幀的相關(guān)屬性值
2.將這些屬性值設(shè)置給指定的對(duì)象
4.1 ValueAnimator(屬性動(dòng)畫最重要的類)
ValueAnimator只實(shí)現(xiàn)了屬性動(dòng)畫應(yīng)該具備的兩個(gè)功能的第一個(gè)昂芜,第二部分由開發(fā)者自行設(shè)置。
即ValueAnimator不會(huì)對(duì)控件做任何操作请梢,我們可以給它設(shè)定從哪個(gè)值運(yùn)動(dòng)到哪個(gè)值,通過監(jiān)聽這些值的漸變過程來自己操作控件力穗。
4.1.1 ValueAnimator構(gòu)造函數(shù)
ValueAnimator的構(gòu)造函數(shù)是空實(shí)現(xiàn)毅弧,一般使用靜態(tài)工廠方法來實(shí)現(xiàn)實(shí)例化。
public static ValueAnimator ofInt(int... values)
public static ValueAnimator ofArgb(int... values)
public static ValueAnimator ofFloat(float... values)
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
參數(shù)類型都是可變參數(shù)長(zhǎng)參數(shù)当窗,所以我們可以傳入任何數(shù)量的值够坐;
傳進(jìn)去的值列表,就表示動(dòng)畫時(shí)的變化范圍崖面;
比如ofInt(2,90,45)就表示從數(shù)值2變化到數(shù)字90再變化到數(shù)字45元咙;
4.1.2 ValueAnimator常用方法
//設(shè)置動(dòng)畫時(shí)長(zhǎng),單位是毫秒
ValueAnimator setDuration(long duration)
//獲取ValueAnimator在運(yùn)動(dòng)時(shí)巫员,當(dāng)前運(yùn)動(dòng)點(diǎn)的值
Object getAnimatedValue();
//開始動(dòng)畫
void start()
//設(shè)置循環(huán)次數(shù),設(shè)置為0表示不循環(huán)庶香,設(shè)置為ValueAnimation.INFINITE表示無限循環(huán)。
void setRepeatCount(int value)
//設(shè)置循環(huán)模式
//取值為ValueAnimation.RESTART時(shí),表示正序重新開始简识,當(dāng)取值為ValueAnimation.REVERSE表示倒序重新開始赶掖。
void setRepeatMode(int value)
//設(shè)置插值器
setInterpolator(xxxx)
//取消動(dòng)畫
void cancel()
// 延時(shí)多久時(shí)間開始,單位是毫秒
public void setStartDelay(long startDelay)
//完全克隆一個(gè)ValueAnimator實(shí)例七扰,包括它所有的設(shè)置以及所有對(duì)監(jiān)聽器代碼的處理
public ValueAnimator clone()
4.1.3 ValueAnimator監(jiān)聽器(2種)
監(jiān)聽器一:監(jiān)聽動(dòng)畫變化時(shí)的實(shí)時(shí)值
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//添加方法
public void addUpdateListener(AnimatorUpdateListener listener)
//移除方法
public void removeUpdateListener(AnimatorUpdateListener listener);
public void removeAllUpdateListeners();
//===代碼實(shí)現(xiàn)===
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int)animation.getAnimatedValue();
tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
}
});
監(jiān)聽器二:監(jiān)聽動(dòng)畫變化時(shí)四個(gè)狀態(tài)
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加方法
public void addListener(AnimatorListener listener)
//移除方法
void removeListener(AnimatorListener listener);
void removeAllListeners();
//===代碼實(shí)現(xiàn)===
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.d("xl","animation start");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d("xl","animation end");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.d("xl","animation cancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.d("xl","animation repeat");
}
});
4.1.4 ValueAnimator代碼實(shí)現(xiàn)(5種)
1?? ofInt(只能傳入Integer類型的值)
ValueAnimator valueAnimatorInt = ValueAnimator.ofInt(0, 400);
valueAnimatorInt.setDuration(1000);
valueAnimatorInt.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int) animation.getAnimatedValue();
tvAnimPropertyValue.layout(curValue,curValue,curValue+tvAnimPropertyValue.getWidth(),curValue+tvAnimPropertyValue.getHeight());
}
});
valueAnimatorInt.start();
2?? ofFloat(只能傳入Float類型的值)
ValueAnimator valueAnimatorFloat = ValueAnimator.ofFloat(0f, 400f, 50f, 300f);
valueAnimatorFloat.setDuration(3000);
valueAnimatorFloat.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
int curValue = (int) animatedValue;
tvAnimPropertyValue.layout(curValue,curValue,curValue+tvAnimPropertyValue.getWidth(),curValue+tvAnimPropertyValue.getHeight());
}
});
valueAnimatorFloat.start();
3?? ofArgb
4?? ofObject(可以傳進(jìn)去任何類型的變量)
構(gòu)造方法解釋
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
/*兩個(gè)參數(shù)奢赂,第一個(gè)是自定義的Evaluator,第二個(gè)是可變長(zhǎng)參數(shù)戳寸,Object類型的呈驶;
為什么要強(qiáng)制傳進(jìn)去自定義的Evaluator拷泽?
Evaluator的作用是根據(jù)當(dāng)前動(dòng)畫的顯示進(jìn)度疫鹊,計(jì)算出當(dāng)前進(jìn)度下把對(duì)應(yīng)的值袖瞻。
既然Object對(duì)象是我們自定的,那必然從進(jìn)度到值的轉(zhuǎn)換過程也必須由我們來做拆吆,不然系統(tǒng)不知道要轉(zhuǎn)成什么鬼聋迎。*/
實(shí)例
ValueAnimator valueAnimatorObject = ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));
valueAnimatorObject.setDuration(10000);
valueAnimatorObject.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
char animatedValue = (char) animation.getAnimatedValue();
tvAnimPropertyValue.setText(String.valueOf(animatedValue));
}
});
valueAnimatorObject.start();
public class CharEvaluator implements TypeEvaluator<Character> {
@Override
public Character evaluate(float fraction, Character startValue, Character endValue) {
int startInt = (int)startValue;
int endInt = (int)endValue;
int curInt = (int)(startInt + fraction *(endInt - startInt));
char result = (char) curInt;
return result;
}
}
5?? ofPropertyValuesHolder
參考ObjectAnimator的ofPropertyValuesHolder的實(shí)現(xiàn)
4.1.5 首先理解interpolator和Evaluator的功能和關(guān)系
ValueAnimator anim = ValueAnimator.ofInt(100, 400);
anim.setDuration(1000);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
在添加了AnimatorUpdateListener的監(jiān)聽以后,通過在監(jiān)聽函數(shù)中調(diào)用 animation.getAnimatedValue()就可以得到當(dāng)前的值枣耀;
那當(dāng)前的值是怎么來的呢霉晕?
當(dāng)前的值 = 100 + (400 - 100)* 顯示進(jìn)度
- 100和400就是我們?cè)O(shè)置的ofInt(100,400)中的值
- 顯示進(jìn)度是通過interpolator得到 [interpolator將時(shí)間變化(0-1)轉(zhuǎn)化成顯示進(jìn)度(0-1,但也可以大于1小于0)]
- 而整個(gè)公式當(dāng)前的值 = 100 + (400 - 100)* 顯示進(jìn)度是在Evaluator中計(jì)算的。
關(guān)系圖如下
4.1.6 interpolator插值器
插值器用來將時(shí)間進(jìn)度轉(zhuǎn)換成顯示進(jìn)度
1.系統(tǒng)自帶插值器
意義如下:
AccelerateDecelerateInterpolator 在動(dòng)畫開始與介紹的地方速率改變比較慢捞奕,在中間的時(shí)候加速
AccelerateInterpolator 在動(dòng)畫開始的地方速率改變比較慢牺堰,然后開始加速
AnticipateInterpolator 開始的時(shí)候向后然后向前甩
AnticipateOvershootInterpolator 開始的時(shí)候向后然后向前甩一定值后返回最后的值
BounceInterpolator 動(dòng)畫結(jié)束的時(shí)候彈起
CycleInterpolator 動(dòng)畫循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線
DecelerateInterpolator 在動(dòng)畫開始的地方快然后慢
LinearInterpolator 以常量速率改變
OvershootInterpolator 向前甩一定值后再回到原來位置
使用
ValueAnimator valueAnimatorInt = ValueAnimator.ofInt(0, 800);
valueAnimatorInt.setInterpolator(new BounceInterpolator());
....
2.自定義插值器
先看看系統(tǒng)加速器LinearInterpolator的寫法:
//以常量速率改變的加速器LinearInterpolator
public class LinearInterpolator implements Interpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
}
//接口Interpolator(LinearInterpolator實(shí)現(xiàn)了Interpolator接口)
public interface Interpolator extends TimeInterpolator {
}
//(Interpolator接口則直接繼承自TimeInterpolator)
public interface TimeInterpolator {
float getInterpolation(float input);
/*
參數(shù)input:
input參數(shù)是一個(gè)float類型颅围,它取值范圍是0到1伟葫,表示當(dāng)前動(dòng)畫的時(shí)間進(jìn)度(與我們的任何設(shè)置無關(guān)).
取0時(shí)表示動(dòng)畫剛開始,取1時(shí)表示動(dòng)畫結(jié)束院促,取0.5時(shí)表示動(dòng)畫中間的位置筏养,其它類推。
返回值:
表示當(dāng)前實(shí)際想要顯示的進(jìn)度常拓。取值可以超過1也可以小于0渐溶。
超過1表示已經(jīng)超過目標(biāo)值,小于0表示小于開始位置弄抬。
*/
}
從上面可以看到茎辐,LinearInterpolator在getInterpolation函數(shù)中,直接把input值返回掂恕,即以當(dāng)前動(dòng)畫的進(jìn)度做為動(dòng)畫的數(shù)值進(jìn)度荔茬,這也就表示當(dāng)前動(dòng)畫的數(shù)值進(jìn)度與動(dòng)畫的時(shí)間進(jìn)度一致,比如竹海,如果當(dāng)前動(dòng)畫進(jìn)度為0慕蔚,那動(dòng)畫的數(shù)值進(jìn)度也是0,那如果動(dòng)畫進(jìn)度為0.5斋配,那動(dòng)畫的數(shù)值進(jìn)度也是在0.5孔飒,當(dāng)動(dòng)畫結(jié)束,動(dòng)畫的進(jìn)度就變成1了艰争,而動(dòng)畫的數(shù)值進(jìn)度也是1了坏瞄。
則自定義Interpolator
public class MyInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return 1-input;
}
}
使用
ValueAnimator valueAnimatorInt = ValueAnimator.ofInt(0, 800);
valueAnimatorInt.setInterpolator(new MyInterpolator());
...
4.1.7 Evaluator
Evaluator是根據(jù)加速器返回的小數(shù)進(jìn)度轉(zhuǎn)換成當(dāng)前數(shù)值進(jìn)度所對(duì)應(yīng)的值。
1.系統(tǒng)自帶Evaluator
Evaluator是專用的甩卓。
Evalutor一般來講不能通用鸠匀,會(huì)報(bào)強(qiáng)轉(zhuǎn)錯(cuò)誤,也就是說逾柿,只有在數(shù)值類型相同的情況下缀棍,Evalutor才能共用宅此。
ofInt()對(duì)應(yīng)的Evaluator類名叫IntEvaluator
ofFloat()對(duì)應(yīng)的Evaluator類名叫FloatEvaluator
ofInt和ofFloat都是系統(tǒng)直接提供的函數(shù),所以在使用時(shí)都會(huì)有默認(rèn)的加速器和Evaluator來使用的爬范,不指定則使用默認(rèn)的父腕;
ArgbEvalutor是用來做顏色值過渡轉(zhuǎn)換的∏嗥伲可配合使用ofInt()
ValueAnimator valueAnimatorIntArgb = ValueAnimator.ofInt(0xffffff00, 0xff0000ff);
valueAnimatorIntArgb.setDuration(3000);
valueAnimatorIntArgb.setEvaluator(new ArgbEvaluator());
valueAnimatorIntArgb.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int)animation.getAnimatedValue();
tvAnimPropertyValue.setBackgroundColor(curValue);
}
});
valueAnimatorIntArgb.start();
2.自定義Evaluator
看看IntEvaluator內(nèi)部是怎么實(shí)現(xiàn):
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
/*
其中fraction就是加速器中的返回值璧亮,表示當(dāng)前動(dòng)畫的數(shù)值顯示進(jìn)度,百分制的小數(shù)表示斥难。
startValue和endValue分別對(duì)應(yīng)ofInt(int start,int end)中的start和end的數(shù)值枝嘶;
*/
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
//也就是 當(dāng)前的值 = 100 + (400 - 100)* 顯示進(jìn)度
}
}
自定義Evaluator
public class MyEvaluator implements TypeEvaluator<Integer> {
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int) (200 + startInt + fraction * (endValue - startInt));
}
}
3.使用
ValueAnimator valueAnimatorInt = ValueAnimator.ofInt(0, 800);
valueAnimatorInt.setEvaluator(new IntEvaluator());
...
4.1.8 ValueAnimator XML實(shí)現(xiàn)
標(biāo)簽<animator />對(duì)應(yīng)ValueAnimator
4.1.8.1 animator所有的字段及取值范圍
<animator
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]
android:interpolator=["@android:interpolator/XXX"]/>
android:valueType:表示參數(shù)值類型,取值為intType和floatType哑诊;
與android:valueFrom躬络、android:valueTo相對(duì)應(yīng)。
如果這里的取值為intType搭儒,那么android:valueFrom穷当、android:valueTo的值也就要對(duì)應(yīng)的是int類型的數(shù)值。
非常注意的是淹禾,如果android:valueFrom馁菜、android:valueTo的值設(shè)置為color類型的值,那么不需要設(shè)置這個(gè)參數(shù)铃岔;
4.1.8.2 代碼實(shí)現(xiàn)
1.在res/animator目錄下創(chuàng)建animator_value.xml
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="300"
android:valueType="intType"
android:duration="1000"
android:interpolator="@android:anim/bounce_interpolator"
/>
2.加載到程序中
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animtor_value);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int offset = (int) animation.getAnimatedValue();
tvAnimPropertyValue.layout(offset,offset,offset+tvAnimPropertyValue.getWidth(),offset+tvAnimPropertyValue.getHeight());
}
});
animator.start();