Android 動畫
標(biāo)簽(空格分隔): android animation
動畫基礎(chǔ)
Android 基礎(chǔ)動畫分為 alpha(透明度)、scale(伸縮)、translate(位移)、rotate(旋轉(zhuǎn))
需要注意:的是,View 中包含了基礎(chǔ)動畫的相關(guān)操作,所以一般的 View 控件都能夠進(jìn)行簡單的動畫操作(View Animation)。
button.setScaleX(伸縮量)
動畫的分類
1.View Animation -- (補(bǔ)間動畫 Tween Animation & 幀動畫 Frame Animation)
2.Property Animation -- (屬性動畫 ValueAnimator & ObjectAnimator)
View動畫的實現(xiàn)方式
- 通過 xml 文件定義動畫;
- 通過創(chuàng)建相應(yīng)的動畫對象對動畫進(jìn)行設(shè)置;
xml 文件實現(xiàn)方式
xml 文件中設(shè)置動畫的效果的屬性格式是 from* ,to* 從某一個數(shù)字變化到另一個數(shù)值,Studio 中會有提示,另外會有沒提示的屬性如下
android:duration="time" 設(shè)置動畫的持續(xù)時間以毫秒為單位
android:fillAfter="T/F" 設(shè)置是否保持動畫結(jié)束狀態(tài)
android:fillBefore="T/F" 設(shè)置是否還原動畫初始狀態(tài) (與 fillEnable 的效果一致)
android:repeatMode="restart/reverse" 設(shè)置回放類型 重新開始/反轉(zhuǎn)
此外還需要注意:一個屬性:android:pivotX="" / android:pivotY=""
這個屬性表示縮放起點 X/Y 軸坐標(biāo)(數(shù)值、百分比、百分比p 三種樣式)
· 為數(shù)值(50)時,表示在當(dāng)前 view 的左上角,在對應(yīng)方向加上 50 作為起始點
· 為百分比(50%)時,表示在當(dāng)前控件的左上角,在對應(yīng)方向加上自己寬度的 50% 作為起始點
· 為百分比p(50%p)時,表示在當(dāng)前控件的左上角,在對應(yīng)方向加上父控件寬度的 50% 作為起始點
若希望能夠使用組合動畫,則需要定義 <set> 標(biāo)簽,設(shè)置 duration,再包含所需要的動畫標(biāo)簽就可以實現(xiàn)簡單的組合動畫
<!-- 簡單組合動畫實現(xiàn) -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<scale
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"/>
<!-- 0~360表示旋轉(zhuǎn)一圈 -->
<rotate
android:fromDegrees="0"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
動畫的 xml 文件需要存放在 res/anim 文件夾下,調(diào)用方式(以 ScaleAnimation 為例)
ScaleAniamtion scaleAnimation = Animation.loadAnimation(context,R.anim.animationName);
view.startAnimation(scaleAnimation);
動畫可以指定 Interpolator 屬性來控制動畫的變化過程(普通動畫都繼承了這個屬性)
AccelerateDecelerateInterpolator 在動畫開始與結(jié)束的地方速率改變比較慢,在中間的時候加速
AccelerateInterpolator 在動畫開始的地方速率改變比較慢,然后開始加速
AnticipateInterpolator 開始的時候向后然后向前甩
AnticipateOvershootInterpolator 開始的時候向后然后向前甩一定值后返回最后的值
BounceInterpolator 動畫結(jié)束的時候彈起
CycleInterpolator 動畫循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線
DecelerateInterpolator 在動畫開始的地方快然后慢
LinearInterpolator 以常量速率改變
OvershootInterpolator 向前甩一定值后再回到原來位置
代碼形式動態(tài)生成動畫以及 Interpolator
四種動畫都有對應(yīng)的 Animation 類均繼承自 Animation,動畫的創(chuàng)建均體現(xiàn)在創(chuàng)建對象的時候,形參包含 context 的構(gòu)造方法基本不用,因為可以進(jìn)行自定義動畫。
此外需要注意: AnimationSet 這個類,構(gòu)造是需要指定是否定義一個 Iterpolator 供 Set 下的動畫共用,若為 false 那么動畫可以定義各自的插值器。
// 這里以 AnimationSet 為例實現(xiàn)上面 xml 的效果
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1.4f,0,1.4f, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
RotateAnimation rotateAnimation = new RotateAnimation(0,720f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.setDuration(3000);
animationSet.setFillAfter(true);
// 使用 Interpolator 上面提到的 Interpolator 可以直接創(chuàng)建起對象進(jìn)行調(diào)用就有對應(yīng)的效果
animationSet.setInterpolator(new AccelerateDecelerateInterpolator());
屬性動畫出現(xiàn)原因:
1.Property Animator 能夠?qū)崿F(xiàn)View Animation無法實現(xiàn)的功能
2.View Animation 僅能對指定控件做動畫而 Property Animator 是用過改變控件某一屬性來做動畫的
(
View Animation 可以對控件做動畫,但實際的屬性信息如位置、顏色等改變不了
Property Animation 就是改變控件的屬性達(dá)到動畫的效果如位置、顏色
)
ValueAnimator
概述:這個類不會對控件做任何操作,是通過監(jiān)聽值的漸變過程來操作控件,所以需要監(jiān)聽 ValueAnimator 的動畫過程來對控件做操作
// 使用例子
ValueAnimator animator = ValueAnimator.ofInt(0,400);
// 設(shè)置動畫的重復(fù)模式
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
// 設(shè)置動畫的重復(fù)次數(shù) INFINITE 是無限循環(huán)
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
animator.setDuration(1000);
// 指定彈跳插值器
valueAnimator.setInterpolator(new BounceInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//TODO 獲取 animation 中值的變化放到控件中
int value = (int)animation.getAnimatedValue();
// 改變控件的位置(左、上、右、下)
tvShow.layout(tvShow.getLeft(),value,tvShow.getRight(),value+tvShow.getHeight());
}
});
animator.start();
獲取 ValueAnimator 的對象有 ofInt() ofFloat(),參數(shù)是一個可變長參數(shù)列表,比如傳入 param1,param2,param3 那么值的變化就是從 param1 -> param2 -> param3
cancel()
-- 用于停止動畫的播放并將往后的動畫取消掉
ValueAnimator 的監(jiān)聽器:
addUpdateListener(AnimatorUpdateListener) & addListener(AnimatorListener)
AnimatorUpdateListener
-- 監(jiān)聽動畫的變化過程,在回調(diào)里可以獲取動畫在整個過程中的值;
AnimatorListener
-- 主要監(jiān)聽 start、end、cancel、repeat 四個狀態(tài);
通過上面的兩個方法可以為 ValueAnimator 添加監(jiān)聽器,移除監(jiān)聽器可以通過以下方法:removeListener(AnimatorListener listener) / removeAllListeners()
removeUpdateListener(AnimatorUpdateListener listener) / removeAllUpdateListeners()
前者用于移除指定監(jiān)聽器,后者將所有的監(jiān)聽器都移除。若將監(jiān)聽器移除,監(jiān)聽器中的操作將不會執(zhí)行,需要區(qū)分取消監(jiān)聽器和取消動畫的區(qū)別,若取消的監(jiān)聽器中沒有包含對動畫狀態(tài)的操作,那么動畫還是會繼續(xù)執(zhí)行只不過沒有了對這個動畫的監(jiān)聽而已。
// ValueAnimator 其他函數(shù)
/**
* 延時多長時間開始,單位:毫秒
*/
public void setStartDelay(long startDelay)
/**
* 完全克隆一個 ValueAnimator 實例,包括它所有的設(shè)置以及所有對監(jiān)聽器代碼的處理
*/
public ValueAnimator clone()
自定義插值器
所有已有的 Interpolator 都是實現(xiàn)了 TimeInterpolator 這個接口,而這個接口中有一個方法
float getInterpolation(float input);
參數(shù) input 取值范圍 0 到 1,表示當(dāng)前動畫的進(jìn)度
· 取 0 時表示動畫開始
· 取 1 時表示動畫結(jié)束
· 取 0.5 時表示動畫中間的位置
返回值表示當(dāng)前實際想要顯示的進(jìn)度,取值可以超過 1 也可以小于 0
· 超過 1 表示已經(jīng)超過目標(biāo)值
· 小于 0 表示小于開始位置
注意:這個方法的形參跟程序員自身設(shè)定的值沒有一點關(guān)系,這個參數(shù)就是表示動畫整個執(zhí)行過程的執(zhí)行時間。
/**
* 簡單實現(xiàn)自定義一個插值器實現(xiàn)跟 LinearInterpolator 相反的變化過程
*/
public class SelfDefineInterpolator implements TimeInterpolator{
@Override
public float getInterpolation(float input) {
return 1-input;
}
}
Evaluator(轉(zhuǎn)換器)
作用:將 Interpolator 返回的百分值轉(zhuǎn)換為具體的數(shù)值提供給監(jiān)聽器進(jìn)行調(diào)用
過程:
ofInt(0,400) -> Interpolator -> Eva
注意: Evaluator 對應(yīng)的數(shù)值類型是專用的(使用 int 進(jìn)行運算那么返回也是 int)
/* 調(diào)用轉(zhuǎn)換器 ofInt() 的前提就調(diào)用 IntEvaluator,ofInt 跟 ofFloat 都會默認(rèn)調(diào)用對應(yīng)的 Evaluator,此外還有一個顏色變化 -- (ArgbEvaluator)*/
animator.setEvaluator(new IntEvaluator());
/**
* 自定義 Evaluator,在原本的 IntEvaluator 基礎(chǔ)上加了 200 的起始值
* 即傳入(0,400)區(qū)間的話在這個轉(zhuǎn)換器中的實際區(qū)間為(200,600)
*/
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));
}
}
因此可以通過重寫 Interpolator 改變數(shù)值進(jìn)度來改變數(shù)值位置,也可以通過改變 Evaluator 中進(jìn)度所對應(yīng)的數(shù)值來改變數(shù)值位置,在 Interpolator 中的修改遷移到 Evaluator 的效果是一樣的。
簡述 AgrbEvaluator (a -- alpha;r -- red;g -- green;b -- blue)
這個類的 evaluator() 返回 int 類型的數(shù)據(jù)就是說可以使用 ofInt() 進(jìn)行調(diào)用該轉(zhuǎn)換器;
傳參需要傳入十六進(jìn)制的顏色值(0xffffffff);ofObject() 概述
由于 ofInt() 和 ofFloat() 這兩個函數(shù)分別只能接受 int 類型和 float 類型的值,如果想要使用如字符串的變換時這兩個方法就不能夠滿足需求了,因此出現(xiàn)這個方法。
/** 源碼,ofObject() 的實現(xiàn)就是創(chuàng)建一個 ValueAnimator 對象并對其進(jìn)行初始化賦值
* 反觀 ofInt 和 ofFloat 都是將 values 傳遞給 ValueAnimator 對象然后返回
* 只不過 ofObject 這個方法多了一個求值程序(evaluator)
*/
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values){
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
ofObject() 這個方法需要指定求值程序是為了方便開發(fā)者在傳入自定義對象進(jìn)行動畫改變的時候也自定義一個對應(yīng)的求值程序(Evaluator),以便系統(tǒng)的執(zhí)行,因為系統(tǒng)提供的 Evaluator 不一定能滿足自定義對象值的變化。
這個自定義的方法通常與自定義 view 結(jié)合使用,基本步驟如下:
/**
* · 自定義用于承載變化數(shù)值的類
* · 自定義用于產(chǎn)生動畫變化的 view
* · 畫出該 view
* · 自定義 Evaluator
* · 實現(xiàn)動畫
* 調(diào)用步驟:使用自定義的 view 調(diào)用動畫的方法即可
* /
// 代碼實現(xiàn)
// 實體類
public class Point {
private int radius;
public Point(int radius) {
this.radius = radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getRadius() {
return radius;
}
}
// 自定義 view
public class MyPointView extends View {
private Point mCurPoint;
public MyPointView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCurPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);
}
}
public void doPointAnim(){
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.start();
}
}
// 求值類
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int start = startValue.getRadius();
int end = endValue.getRadius();
int curValue = (int) (start + fraction * (end - start));
return new Point(curValue);
}
}
ObjectAnimator 使用
由于 ValueAnimator 只是針對屬性值的變化不能修改整個控件的屬性因此派生出 ObjectAnimator 因此 ValueAnimator 的方法 ObjectAnimator 都有。
一般用法就是在構(gòu)建 ObjectAnimator 的時候同時指定需要執(zhí)行動畫的控件和執(zhí)行的動畫。
// 源碼,values 表示變化過程,源碼中將 values 的數(shù)據(jù)添加到 ObjectAnimator 中
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
// 例子:透明度從 1 到 0 再到 1
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"alpha",1,0,1);
objectAnimator.setDuration(1000);
objectAnimator.start();
流程
ofFloat(**) -> 加速器 -> Evaluator -> 調(diào)用 set 函數(shù)
最后一步調(diào)用 set 函數(shù)是根據(jù)屬性拼裝 set 函數(shù)后反射調(diào)用,并將當(dāng)前值作為參數(shù)傳入(set 函數(shù)拼裝的意思就是將屬性的首字母大寫然后拼接在 set 之后就成了設(shè)置某個屬性值的函數(shù),拼裝成之后在通過反射調(diào)用該函數(shù))
注意:ObjectAnimator 只負(fù)責(zé)把當(dāng)前運動動畫的數(shù)值傳給 set 函數(shù),至于 set 函數(shù)里面怎么做可以自行定義
// 以上面的 MyPointView 為例實現(xiàn)一個 ObjectAnimator
// MyPointView 中添加/修改以下代碼
private Point mObjPoint = new Point(100);
@Override
protected void onDraw(Canvas canvas) {
if(mObjPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mObjPoint.getRadius(),paint);
}
super.onDraw(canvas);
}
/**
* 設(shè)置半徑,供反射調(diào)用
* @param radius
*/
void setPointRadius(int radius){
mObjPoint.setRadius(radius);
invalidate();
}
// 實際使用
private void doViewPointAnim() {
// 這個 propertyName 就是自己定義在 MyPointView 定義的 setPointRadius() set 后面的屬性
objectAnimator = ObjectAnimator.ofInt(myPointView,"pointRadius",0,300,100);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.start();
}
ObjectAnimator 會通過反射調(diào)用對應(yīng) view 的 setPropertyName() 方法,從一個完整的 bean 而言必然會有一個 getPropertyName() 方法,那么什么時候 ObjectAnimator 會調(diào)用這個 get 方法呢?就是當(dāng)開發(fā)者在實例化 ObjectAnimator 對象的時候傳入一個屬性值的時候(通常 ObjectAnimator 是使用一組數(shù)據(jù)來進(jìn)行數(shù)據(jù)的變化)因此當(dāng)傳入一個屬性值的時候它會提示警告沒有初值所以默認(rèn)會使用定義的類型的默認(rèn)值(比如 int 值的默認(rèn)值是 0),那使用 ofInt() 實例化對象時僅指定了一個值那么就會使用默認(rèn)值作為變化的起始值。若想要改變默認(rèn)初始值的話可以添加 getPropertyName() 自定義初始值,如本例
/**
* 設(shè)置初始值,供反射調(diào)用
* @return
*/
public int getPointRadius(){
return 50;
}
上述提到的動畫操作中包含了動畫的基本操作,但是我們發(fā)現(xiàn)每一個 Animator 無論是 Value 還是 Object 的對象顯示單一的動畫比如旋轉(zhuǎn)、漸變等效果,那么就會有疑問說能不能在代碼中將動畫整合起來然后一起顯示呢?(就像 xml 中設(shè)置 set 動畫集一樣)答案是肯定的。
Google 應(yīng)對這一需求提供了 PropertyValuesHolder 這個 API,它就保存了動畫過程中所需要操作的屬性和對應(yīng)的值。
當(dāng)我們需要組合動畫的時候需要使用 ofPropertyValuesHolder 這個方法去構(gòu)建 ObjectAnimator 這個類的對象(因為 ObjectAnimator 派生自 ValueAnimator 所以它也有這個方法)通過這個方法構(gòu)建 ObjectAnimator 的話就需要 PropertyValuesHolder 這個類的對象,因此需要找到創(chuàng)建這個類的對象的方法,觀察源碼我們可以發(fā)現(xiàn),創(chuàng)建這個類的對象的方法跟 ObjectAnimator 創(chuàng)建對象的方法名字是一致的(提供了一定的便利程度)
// PropertyValuesHolder 的創(chuàng)建對象的方法
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values) // 跟 Animator 一樣可以引入 Evaluator
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
// 獲取到 PropertyValuesHolder 的對象之后就可以 ObjectAnimator 的 ofPropertyValuesHolder 創(chuàng)建對象了
public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values){
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
----------------------------------源碼、例子分割線-------------------------------------
/**
* 使用例子
* 返回 animator 這個對象是為了方便取消動畫
*/
private ObjectAnimator rotateAndChange(){
PropertyValuesHolder rotateValuesProperty = PropertyValuesHolder.ofFloat("Rotation",60f,-60f,40f,-40f,-20f,20f,10f,-10f,0f);
PropertyValuesHolder rgbValuesProperty = PropertyValuesHolder.ofInt("BackgroundColor",0xffffffff,0xffff00ff,0xfffff00,0xffffffff);
objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tvShow,rotateValuesProperty,rgbValuesProperty);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
return objectAnimator;
}
PropertyValuesHolder 的 KeyFrame(關(guān)鍵幀)
一個 30 秒的動畫根據(jù)一秒要播放 24 幀的圖片來做的話,如果要做那么多的圖片是不科學(xué)的, 因此引入關(guān)鍵幀的概念:一個 30 秒的動畫只需要定義開始時候的關(guān)鍵幀和結(jié)束之后的關(guān)鍵幀,過程由系統(tǒng)去填充。
因此一個關(guān)鍵幀不許包含兩個原素:時間點和位置 (即表示某物體在某時間點應(yīng)該在哪個位置上)
/**
* Keyframe 初始化對象的方法
* fraction:表示當(dāng)前的顯示進(jìn)度
* value:表示當(dāng)前應(yīng)該在的位置
*/
public static Keyframe ofFloat(float fraction, float value)
// Keyframe 的生成方式
Keyframe kf = KeyFrame.ofFloat(0,0); // 表示動畫進(jìn)度為 0 時,數(shù)值位置是 0(進(jìn)度,位置)
使用 Keyframe 的基本步驟跟 PropertyValuesHolder 類似
· 生成 Keyframe 對象;
· 生成 PropertyValuesHolder 對象;
· 生成 ObjectAnimator 對象;
// 例子
private ObjectAnimator doKeyframeAnim(){
Keyframe keyframeStart = Keyframe.ofFloat(0,0);
Keyframe keyframeMiddle = Keyframe.ofFloat(0.1f,-20f);
Keyframe keyframeEnd = Keyframe.ofFloat(1f,0);
PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",keyframeStart,keyframeMiddle,keyframeEnd);
objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tvShow,propertyValuesHolder);
objectAnimator.setDuration(1000);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
return objectAnimator;
}
需要注意的是 keyframe 設(shè)置插值器的作用是:從上一幀開始到當(dāng)前設(shè)置插值器的幀時,這個過程值的計算是使用指定的插值器進(jìn)行計算。不要給初始幀設(shè)置插值器因為他說第一幀,設(shè)置了也沒有用。
Keyframe keyframeStart = Keyframe.ofFloat(0,0);
Keyframe keyframeMiddle = Keyframe.ofFloat(0.1f,-20f);
// 表示 start 幀到 middle 幀使用彈跳插值器
keyframeMiddle.setInterprolater(new BounceInterpolator());
Keyframe 的 ofObject 產(chǎn)生 keyframe 對象這個自定義的方式跟其他類的 ofObject 方法的自定義方式是一致的,定義開始時、過程中、結(jié)束時的關(guān)鍵幀
這三個狀態(tài)的幀存在的情況:
· 去掉第 0 幀,將以第一個關(guān)鍵幀為起始位置
· 去掉結(jié)束幀,將以最后一個關(guān)鍵幀為結(jié)束位置
· 使用 keyframe 構(gòu)建動畫至少要有兩個或兩個以上的幀
AnimatorSet
之前用到的 PropertyValuesHolder 的組合動畫是作用在一個控件上的,當(dāng)我們有需求要進(jìn)行多個控件的動畫進(jìn)行播放的時候就需要 AnimatorSet 這個類將,可以將各種 Animator 都放進(jìn)這個 AnimatorSet 中然后進(jìn)行播放,
播放有兩種方式:1、playSequentially(所有動畫依次播放);2、playTogether(所有動畫一起播放)
兩個函數(shù)的參數(shù)都有可變參數(shù)的 Animator 或者傳入集合(一般使用可變參數(shù)比較方便)
(注意在使用 AnimatorSet 的時候不要與 AniamtionSet 弄混淆了,Animator 跟 Animation 是不一樣的,Animation 處理的是補(bǔ)間動畫,Animator 處理的是屬性動畫,AnimatorSet 設(shè)置之后會覆蓋單個 ObjectAbunator)
/*
* playSequentially 例子
* 注意,這個方法的實現(xiàn)是調(diào)用了 play(animator-n).before(animator-n+1) 這兩個發(fā)放
* 這句代碼就是說在執(zhí)行第 n+1 個動畫之前執(zhí)行第 n 個動畫
* playSequentially 與 playTogether 的實現(xiàn)也是使用了這種鏈?zhǔn)秸{(diào)用
* 所以 animatorSet 也支持這種鏈?zhǔn)秸{(diào)用
*/
private void doPlaySequentiallyAnimator(){
ObjectAnimator objectAnimatorAlpha = ObjectAnimator.ofFloat(tvShow,"alpha",1,0,1);
ObjectAnimator objectAnimatorRotation = ObjectAnimator.ofFloat(btnStart,"rotation",0,720);
ObjectAnimator objectAnimatorScalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",1,2);
ObjectAnimator objectAnimatorRescalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",2,1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playSequentially(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
animatorSet.start();
}
// playTogether 例子
private void doPlayTogetherAnimator(){
ObjectAnimator objectAnimatorAlpha = ObjectAnimator.ofFloat(tvShow,"alpha",1,0,1);
ObjectAnimator objectAnimatorRotation = ObjectAnimator.ofFloat(btnStart,"rotation",0,720);
ObjectAnimator objectAnimatorScalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",1,2);
ObjectAnimator objectAnimatorRescalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",2,1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playTogether(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
animatorSet.start();
}
注意:
1、playTogether 和 playSequentially 在激活動畫后,控件的動畫情況與這兩個方法無關(guān),這兩個方法只負(fù)責(zé)激活動畫;
2、playSequentially 只有上一個控件做完動畫之后,才會激活下一個控件的動畫,若上一個控件的動畫是無線循環(huán),那下一個控件的動畫將無法進(jìn)行;
此外,AnimatorSet 提供延遲啟動動畫的功能 setStartDelay(long startDelay)
animatorSet.setStartDelay(2000);
AnimatorSet 的延時操作的注意事項
· 延時是延長 AnimatorSet 激活的時間,對單個動畫的延時設(shè)置沒有影響
· 真正激活延時 = startDelay + 第一個動畫.startDelay
· AnimatorSet 激活之后,第一個動畫絕對會開始運行,后面的動畫根據(jù)自己是否延時自行處理Animator 的 xml 實現(xiàn)
標(biāo)簽名
· animator -- ValueAnimator
· objectanimator -- ObjectAnimator
· set -- AnimatorSet
// 將 xml 加在到程序中
ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(context,R.animator.animatorname);
ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(context,R.animator.animatorname);
文章參考自啟艦的 csdn 博客