Android Animation

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)方式

  1. 通過 xml 文件定義動畫;
  2. 通過創(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 博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。