Android 動(dòng)畫(huà)詳解:屬性動(dòng)畫(huà)抚垃、View 動(dòng)畫(huà)和幀動(dòng)畫(huà)

在 Android 中购岗,基本的動(dòng)畫(huà)共有三種類型:

  1. View 動(dòng)畫(huà):也叫視圖動(dòng)畫(huà)或者補(bǔ)間動(dòng)畫(huà),主要是指 android.view.animation 包下面的一些類无埃,只能被用來(lái)設(shè)置給 View徙瓶,缺點(diǎn)是比如當(dāng)控件移動(dòng)之后,接收點(diǎn)擊的控件的位置不會(huì)跟隨移動(dòng)嫉称,并且能夠?qū)崿F(xiàn)的效果只有移動(dòng)侦镇、縮放、旋轉(zhuǎn)和淡入淡出操作四種及其組合织阅。
  2. Drawable 動(dòng)畫(huà):也叫 Frame 動(dòng)畫(huà)或者幀動(dòng)畫(huà)虽缕,其實(shí)可以劃分到視圖動(dòng)畫(huà)的類別,實(shí)現(xiàn)方式是將一些列的 Drawable 像幻燈片一樣一個(gè)一個(gè)地顯示蒲稳。
  3. Property 動(dòng)畫(huà): 屬性動(dòng)畫(huà)主要是指 android.animation 包下面的一些類,只對(duì) API 11 以上版本的Android 系統(tǒng)才有效伍派,但我們可以通過(guò)兼容庫(kù)做低版本兼容江耀。這種動(dòng)畫(huà)可以設(shè)置給任何 Object,包括那些還沒(méi)有渲染到屏幕上的對(duì)象诉植。這種動(dòng)畫(huà)是可擴(kuò)展的祥国,可以讓你自定義任何類型和屬性的動(dòng)畫(huà)。

1晾腔、Drawable 動(dòng)畫(huà)

在這里,我們先對(duì) Drawable 動(dòng)畫(huà)進(jìn)行講解灼擂,因?yàn)樗鄬?duì)于后面的兩種動(dòng)畫(huà)比較簡(jiǎn)單。在示例程序我們準(zhǔn)備了一系列圖片資源剔应,并在 drawable 文件夾下面定義了動(dòng)畫(huà)資源 record_anim.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list android:oneshot="false"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/record0" android:duration="500"/>
    <item android:drawable="@drawable/record1" android:duration="500"/>
    <item android:drawable="@drawable/record2" android:duration="450"/>
    <item android:drawable="@drawable/record3" android:duration="400"/>
    <item android:drawable="@drawable/record4" android:duration="350"/>
    <item android:drawable="@drawable/record5" android:duration="400"/>
    <item android:drawable="@drawable/record6" android:duration="400"/>
</animation-list>

然后睡腿,我們?cè)诖a中使用該資源,并將其賦值給 ImageView碉纺。然后盛撑,我們從該控件中獲取該 Drawable 并將其轉(zhuǎn)換成 AnimationDrawable,隨后我們調(diào)用它的 start() 方法就開(kāi)啟了 Drawable 動(dòng)畫(huà):

getBinding().ivRecord.setImageResource(R.drawable.record_anim);
animDraw = (AnimationDrawable) getBinding().ivRecord.getDrawable();
animDraw.start();

此外晚树,我們可以調(diào)用該 Drawable 的 stop() 方法停止動(dòng)畫(huà)宝鼓。

幀動(dòng)畫(huà)的注意事項(xiàng)

使用幀動(dòng)畫(huà)的時(shí)候要注意設(shè)置的圖片不宜過(guò)多蛉签、過(guò)大,以防止因?yàn)閮?nèi)存不夠而出現(xiàn) OOM狂塘。

2了嚎、View 動(dòng)畫(huà)

2.1 基本 View 動(dòng)畫(huà)

該動(dòng)畫(huà)的資源處在 android.view.animation 包下,主要有以下幾個(gè)類,它們都繼承自 Animation ,我們可以使用它們來(lái)實(shí)現(xiàn)復(fù)雜的動(dòng)畫(huà)幕屹。這些動(dòng)畫(huà)類分別有對(duì)應(yīng)的 xml 標(biāo)簽说敏,所以,我們可以在 xml 中定義動(dòng)畫(huà)等脂,也可以在代碼中實(shí)現(xiàn)動(dòng)畫(huà)效果。這里的 AnimationSet 可以用來(lái)將多個(gè)動(dòng)畫(huà)效果進(jìn)行組合燃异,各預(yù)定義動(dòng)畫(huà)的對(duì)照可以參考下面這張圖表:

Android View 動(dòng)畫(huà)框架

2.2 View 動(dòng)畫(huà)屬性

當(dāng)然雕凹,要實(shí)現(xiàn)一種動(dòng)畫(huà)效果會(huì)有許多屬性需要指定苦锨,在 xml 中,我們用標(biāo)簽的屬性指定,在代碼中我們用對(duì)象的 setter 方法指定。于是杆麸,我們可以得到下面這個(gè)對(duì)應(yīng)關(guān)系:

Android View 動(dòng)畫(huà)屬性詳解

上面的對(duì)應(yīng)關(guān)系是所有的 View 動(dòng)畫(huà)共用的,對(duì)各個(gè)具體的動(dòng)畫(huà)類型還有其獨(dú)有的屬性旦万。你可以在各個(gè)動(dòng)畫(huà)的構(gòu)造方法中淆两,通過(guò)它們從 AttributeSet 中獲取了哪些字段來(lái)了解它們都定義了哪些屬性,這里我們不對(duì)其一一進(jìn)行說(shuō)明商蕴。各預(yù)定義的屬性動(dòng)畫(huà)分別按照不同的方式實(shí)現(xiàn)了 AnimationapplyTransformation 方法腹殿,具體的這些屬性如何使用以及 View 動(dòng)畫(huà)的效果是如何實(shí)現(xiàn)的晒喷,都可通過(guò)閱讀該方法的定義得知雨效。

對(duì)于 AnimationSet据悔,它內(nèi)部維護(hù)了一個(gè) Animation 列表清女,并且其本身也是一個(gè) Animation怜瞒,所以睬捶,AnimationSet 內(nèi)部可以添加子 AnimationSet

2.3 插值器

上文中我們提到過(guò),View 動(dòng)畫(huà)的具體實(shí)現(xiàn)是通過(guò)覆寫(xiě) AnimationapplyTransformation 方法來(lái)完成的。這里我們以 AlphaAnimation 為例來(lái)看它是如何作用的,同時(shí)你應(yīng)該注意插值器的作用原理谋旦。該方法會(huì)在 Animation 中被循環(huán)調(diào)用,調(diào)用的時(shí)候會(huì)根據(jù)插值器計(jì)算出一個(gè)時(shí)間,并將其傳遞到 applyTransformation 方法中窖贤。

AnimationgetTransformation 方法片段:

if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
    if (!mStarted) {
        fireAnimationStart();
        mStarted = true;
        if (NoImagePreloadHolder.USE_CLOSEGUARD) {
            guard.open("cancel or detach or getTransformation");
        }
    }

    if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

    if (mCycleFlip) {
        normalizedTime = 1.0f - normalizedTime;
    }

    final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
    applyTransformation(interpolatedTime, outTransformation);
}

AlphaAnimationapplyTransformation 方法:

protected void applyTransformation(float interpolatedTime, Transformation t) {
    final float alpha = mFromAlpha;
    t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}

顯然砖顷,這里的 interpolatedTime 的是一個(gè)比例。比如赃梧,假如一個(gè)透明動(dòng)畫(huà)需要持續(xù) 10s滤蝠,透明度需要從 0.5f1.0f,而插值的規(guī)則是一個(gè)二次函數(shù)授嘀。那么第 t (0<t<10) 秒的時(shí)候控件的透明度應(yīng)該是:

alpha = 0.5f + (1.0f - 0.5f) * t^2 / 100

以上就是插值器的作用原理几睛,你也可以按照自己的需求實(shí)現(xiàn)自己的插值器,從而實(shí)現(xiàn)期待的動(dòng)畫(huà)效果粤攒。

2.4 使用 View 動(dòng)畫(huà)

作為一個(gè)例子所森,這里我們實(shí)現(xiàn)一個(gè)讓控件抖動(dòng)的動(dòng)畫(huà)。在 anim 文件夾下面夯接,我們定義一個(gè)平移的動(dòng)畫(huà)焕济,并使用插值器使其重復(fù):

anim/shake.xml 的定義:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="700"
    android:fromXDelta="0.0"
    android:interpolator="@anim/cycle_7"
    android:toXDelta="15.0" />

插值器 anim/cicle_7.xml 的定義:

<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:cycles="4.0" />

然后,我們?cè)诖a中加載 Animation 并調(diào)用控件的 startAnimation() 方法開(kāi)啟動(dòng)畫(huà):

getBinding().v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake));

對(duì)于 View 盔几,我們有 startAnimation() 用來(lái)對(duì) View 開(kāi)始動(dòng)畫(huà)晴弃;有 clearAnimation() 用來(lái)取消 View 在執(zhí)行的動(dòng)畫(huà)。

不使用 xml逊拍,僅使用代碼我們一樣可以實(shí)現(xiàn)上述的效果上鞠,這里我們不再進(jìn)行說(shuō)明。

2.5 View 動(dòng)畫(huà)的特殊使用場(chǎng)景

2.5.1 LayoutAnimation

LayoutAnimation 作用于 ViewGroup芯丧,可以使其子元素出場(chǎng)時(shí)都均有某種動(dòng)畫(huà)效果芍阎,通常用于 ListView。我們可以像下面這樣定義布局動(dòng)畫(huà):

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation 
    android:delay="500"
    android:animation="@anim/shake"
    xmlns:android="http://schemas.android.com/apk/res/android" />

顯然缨恒,這里我們需要引用一個(gè)其他的動(dòng)畫(huà)谴咸。然后轮听,我們可以在 ListViewlayoutAnimation 屬性中指定布局動(dòng)畫(huà)×爰眩或者調(diào)用 ListViewsetLayoutAnimation() 方法應(yīng)用上述動(dòng)畫(huà)血巍。

2.5.2 Activity 切換

我們可以通過(guò)在 Activity 中調(diào)用 overridePendingTransition(R.anim.shake, R.anim.shake); 方法來(lái)重寫(xiě) Activity 的切換動(dòng)畫(huà)。注意這個(gè)方法應(yīng)該在 startActivity(Intent) 或者 finish() 之后立即調(diào)用珊随。

3述寡、屬性動(dòng)畫(huà)

3.1 基礎(chǔ)梳理

我們可以對(duì)比 View 動(dòng)畫(huà)來(lái)學(xué)習(xí)屬性動(dòng)畫(huà)。

  1. 屬性動(dòng)畫(huà)主要是指 android.animation 包下面的一些類叶洞。
  2. 屬性動(dòng)畫(huà)基礎(chǔ)的動(dòng)畫(huà)類是 Animator鲫凶;屬性動(dòng)畫(huà)也為我們提供了幾個(gè)預(yù)定義的類:AnimatorSet, ObjectAnimator, TimeAnimatorValueAnimator;這幾個(gè)預(yù)定義類之間的繼承關(guān)系是京办,AnimatorSetValueAnimator 直接繼承自 Animator,而 ObjectAnimatorTimeAnimator 繼承自 ValueAnimator帆焕。
  3. 與 View 動(dòng)畫(huà)不同的是惭婿,屬性動(dòng)畫(huà)的使用范圍更加寬泛,它不局限于 View叶雹,本質(zhì)上它是通過(guò)修改控件的屬性值實(shí)現(xiàn)的動(dòng)畫(huà)财饥。當(dāng)你嘗試對(duì)某個(gè)對(duì)象的某個(gè)屬性進(jìn)行修改的時(shí)候會(huì)有一些限制,即屬性動(dòng)畫(huà)要求該對(duì)象必須提供了該屬性的 setter 方法折晦。
  4. 屬性動(dòng)畫(huà)也有 xml 和代碼兩種定義方式钥星,它的 xml 通常定義在 animator 文件夾下面,而 View 動(dòng)畫(huà)定義在 anim 文件夾下面满着。
  5. 屬性動(dòng)畫(huà)提供了類似于 AnimationUtils 的方法用來(lái)從布局文件夾中加載屬性動(dòng)畫(huà):AnimatorInflater 類的 loadAnimator() 方法谦炒。
  6. 屬性動(dòng)畫(huà)也有自己的插值器:TimeInterpolator,并且也提供了幾個(gè)預(yù)定義的插值器风喇。
  7. 我們也可以調(diào)用 View 的方法來(lái)使用屬性動(dòng)畫(huà)宁改,我們可以通過(guò) View 的 animate() 方法獲取一個(gè) ViewPropertyAnimator,然后調(diào)用 ViewPropertyAnimator 的其他方法進(jìn)行鏈?zhǔn)秸{(diào)用以實(shí)現(xiàn)復(fù)雜的屬性動(dòng)畫(huà)效果魂莫。

下面是屬性動(dòng)畫(huà)的代碼實(shí)現(xiàn)和 xml 實(shí)現(xiàn)兩種方式的對(duì)比:

屬性動(dòng)畫(huà)

上文中还蹲,我們總結(jié)了屬性動(dòng)畫(huà)的一些知識(shí),并將其與 View 動(dòng)畫(huà)進(jìn)行了對(duì)比耙考。這里是一個(gè)簡(jiǎn)單的梳理谜喊,在下文中我們會(huì)對(duì)屬性動(dòng)畫(huà)進(jìn)行更加詳細(xì)的介紹。

3.2 使用屬性動(dòng)畫(huà)

3.2.1 ValueAnimator

上面說(shuō)過(guò) ValueAnimatorObjectAnimatorTimeAnimator 的基類倦始,我們可以這樣使用它:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
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();

這里我們使用 log 輸出了值漸變的過(guò)程斗遏,從日志中可以看出它的效果是值從 0 不斷遞增直到 1。如果我們?cè)谶@個(gè)監(jiān)聽(tīng)方法中根據(jù)值修改控件的屬性一樣可以實(shí)現(xiàn)動(dòng)畫(huà)效果鞋邑。除了 ofFloat() 還有 ofInt() 等方法最易,它們的效果相似怒坯。

3.2.2 ObjectAnimator

上面,如果我們想要實(shí)現(xiàn)動(dòng)畫(huà)效果藻懒,需要在 ValueAnimator 的監(jiān)聽(tīng)事件中修改對(duì)象的屬性剔猿,這里的 ObjectAnimator ,我們只需要傳入對(duì)象實(shí)例和屬性的字符串名稱嬉荆,修改對(duì)象屬性的操作就可以自動(dòng)完成归敬。比如下面的程序的效果是控件 textview 的透明度會(huì)在 5s 之內(nèi)從 1 變成 0 再變回 1.

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.start();

注意這里我們傳入的是 alpha,這個(gè)字段本身并不存在于控件中鄙早,而是有一個(gè) setAlpha() 的方法汪茧。也就是說(shuō),ObjectAnimator 作用的原理是通過(guò)反射觸發(fā) setter 方法而不是修改屬性來(lái)實(shí)現(xiàn)的限番。你可以在類 PropertyValuesHolder 中更詳細(xì)地了解這方面的內(nèi)容舱污。

PropertyValuesHolder 包裝了我們要修改的屬性的對(duì)象和方法等信息,然后會(huì)使用反射觸發(fā)指定對(duì)象的方法來(lái)完成對(duì)對(duì)象屬性的修改弥虐。其中

void setupSetter(Class targetClass) {
    Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
    mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}

會(huì)去尋找我們要修改屬性的 setter 方法扩灯,然后

void setAnimatedValue(Object target) {
    if (mProperty != null) {
        mProperty.set(target, getAnimatedValue());
    }
    if (mSetter != null) {
        try {
            mTmpValueArray[0] = getAnimatedValue();
            mSetter.invoke(target, mTmpValueArray);
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}

會(huì)去觸發(fā) setter 方法,以修改對(duì)象的屬性霜瘪。

3.2.3 AnimatorSet

AnimatorSet 內(nèi)部提供了一個(gè)構(gòu)建者 AnimatorSet.Builder 來(lái)幫助我們構(gòu)建組合動(dòng)畫(huà)珠插,AnimatorSet.Builder 提供了下面四種方法:

  1. after(Animator anim):將現(xiàn)有動(dòng)畫(huà)插入到傳入的動(dòng)畫(huà)之后執(zhí)行
  2. after(long delay):將現(xiàn)有動(dòng)畫(huà)延遲指定毫秒后執(zhí)行
  3. before(Animator anim):將現(xiàn)有動(dòng)畫(huà)插入到傳入的動(dòng)畫(huà)之前執(zhí)行
  4. with(Animator anim):將現(xiàn)有動(dòng)畫(huà)和傳入的動(dòng)畫(huà)同時(shí)執(zhí)行

當(dāng)我們調(diào)用 AnimatorSetplay() 方法的時(shí)候就能獲取一個(gè) AnimatorSet.Builder 實(shí)例,然后我們就可以使用構(gòu)建者的方法進(jìn)行鏈?zhǔn)秸{(diào)用了:

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();

3.2.4 TypeEvaluator

正如前文所述颖对,屬性動(dòng)畫(huà)也有自己的插值器捻撑,我們可以通過(guò)插值函數(shù)指定在某個(gè)時(shí)間段內(nèi)屬性改變的速率。插值函數(shù)得到的是一個(gè)比例缤底,是沒(méi)有意義的顾患。在 View 動(dòng)畫(huà)的 AlphaAnimation 中,如果我們指定了起止的透明度个唧,那么我們可以通過(guò)透明度的計(jì)算規(guī)則得到某個(gè)時(shí)刻的透明度描验。但是對(duì)于屬性動(dòng)畫(huà),因?yàn)樗梢詰?yīng)用于任何屬性坑鱼,這個(gè)屬性又可能是任何類型的膘流,那么這個(gè)屬性將采用什么樣的計(jì)算規(guī)則呢?這就需要我們使用 TypeEvaluator 來(lái)指定一個(gè)計(jì)算規(guī)則鲁沥。也就是說(shuō)呼股,TypeEvaluator 是屬性動(dòng)畫(huà)的屬性的計(jì)算規(guī)則。

下面是 TypeEvaluator 的定義画恰,這里的三個(gè)參數(shù)的含義分別是彭谁,fraction 是當(dāng)前的比例,可以通過(guò)插值器計(jì)算得到允扇;startValueendValue 分別是屬性變化的起止值缠局。它的返回結(jié)果就是在某個(gè)時(shí)刻某個(gè)屬性的值则奥。

public interface TypeEvaluator<T> {
    public T evaluate(float fraction, T startValue, T endValue);
}

屬性動(dòng)畫(huà)中已經(jīng)為我們提供了幾個(gè)預(yù)定義的 TypeEvaluator,比如 FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

在屬性動(dòng)畫(huà)的 PropertyValuesHolder 中會(huì)根據(jù)屬性的類型選擇預(yù)定義的 TypeEvaluator狭园。但是如果我們的屬性的類型不在預(yù)定義的范圍之內(nèi)就需要自己實(shí)現(xiàn)一個(gè) TypeEvaluator读处。下面我們以日期類型為例來(lái)實(shí)現(xiàn)一個(gè) TypeEvaluator

當(dāng)我們使用 ValueAnimatorofObject() 方法獲取 ValueAnimator 實(shí)例的時(shí)候唱矛,要求我們傳入一個(gè) TypeEvaluator罚舱,于是我們可以像下面這樣定義:

private static class DateEvaluator implements TypeEvaluator<Date> {

    @Override
    public Date evaluate(float fraction, Date startValue, Date endValue) {
        long startTime = startValue.getTime();
        return new Date((long) (startTime + fraction * (endValue.getTime() - startTime)));
    }
}

然后,我們可以這樣使用它:

ValueAnimator animator = ValueAnimator.ofObject(new DateEvaluator(), new Date(0), new Date());
animator.setDuration(5000);
animator.addUpdateListener(animation -> {
    Date date = (Date) animation.getAnimatedValue();
    LogUtils.d(date);
});
animator.start();

這樣就可以得到在 5s 之內(nèi)輸出的從時(shí)間戳為0绎谦,到當(dāng)前時(shí)刻的所有的日期變化管闷。

3.2.5 TimeInterpolator

就像 View 動(dòng)畫(huà)一樣,我們可以為屬性動(dòng)畫(huà)指定一個(gè)插值器窃肠。插值器的作用是用來(lái)設(shè)置指定時(shí)間段內(nèi)數(shù)值的變化的速率包个。在屬性動(dòng)畫(huà)中,插值器是 TimeInterpolator冤留,同樣也有幾個(gè)默認(rèn)的實(shí)現(xiàn):

  1. AccelerateDecelerateInterolator:先加速后減速碧囊。
  2. AccelerateInterpolator:加速。
  3. DecelerateInterpolator:減速搀菩。
  4. AnticipateInterpolator:先向相反方向改變一段再加速播放呕臂。
  5. AnticipateOvershootInterpolator:先向相反方向改變破托,再加速播放肪跋,會(huì)超出目標(biāo)值然后緩慢移動(dòng)至目標(biāo)值,類似于彈簧回彈土砂。
  6. BounceInterpolator:快到目標(biāo)值時(shí)值會(huì)跳躍州既。
  7. CycleIinterpolator:動(dòng)畫(huà)循環(huán)一定次數(shù),值的改變?yōu)橐徽液瘮?shù):Math.sin(2 * mCycles * Math.PI * input)萝映。
  8. LinearInterpolator:線性均勻改變吴叶。
  9. OvershottInterpolator:最后超出目標(biāo)值然后緩慢改變到目標(biāo)值。

3.2.6 在 xml 中使用屬性動(dòng)畫(huà)

我們可以像下面這樣定義一個(gè)屬性動(dòng)畫(huà)序臂,

<set android:ordering=["together" | "sequentially"]>
    <objectAnimator
        android:propertyName="string"
        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"]/>
    <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"]/>
    <set>
        ...
    </set>
</set>

這里的android:ordering用于控制子動(dòng)畫(huà)啟動(dòng)方式是先后有序的還是同時(shí)進(jìn)行蚌卤,兩個(gè)可選參數(shù): sequentially 表示動(dòng)畫(huà)按照先后順序;together(默認(rèn))表示動(dòng)畫(huà)同時(shí)啟動(dòng)奥秆。

這里的 <objectAnimator> 標(biāo)簽的含義如下:

屬性動(dòng)畫(huà)的XML屬性詳解

這樣在 XML 中定義了屬性動(dòng)畫(huà)之后逊彭,我們可以在代碼中通過(guò)工具類獲取到動(dòng)畫(huà)實(shí)例并使用:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.animtor.property_animator);
set.setTarget(myObject);
set.start();

4、使用動(dòng)畫(huà)的注意事項(xiàng)

  1. 內(nèi)存耗盡:使用幀動(dòng)畫(huà)的時(shí)候防止因?yàn)閳D片過(guò)多導(dǎo)致 OOM构订。
  2. View 動(dòng)畫(huà)并沒(méi)有真正改變 View 的位置View 動(dòng)畫(huà)并沒(méi)有真正改變 View 的屬性侮叮,即 View 動(dòng)畫(huà)執(zhí)行之后并未改變 View 的真實(shí)布局屬性值。譬如我們?cè)诓季种杏幸粋€(gè) Button 在屏幕上方悼瘾,我們?cè)O(shè)置了平移動(dòng)畫(huà)移動(dòng)到屏幕下方然后保持動(dòng)畫(huà)最后執(zhí)行狀態(tài)呆在屏幕下方囊榜,這時(shí)如果點(diǎn)擊屏幕下方動(dòng)畫(huà)執(zhí)行之后的 Button 是沒(méi)有任何反應(yīng)的审胸,而點(diǎn)擊原來(lái)屏幕上方?jīng)]有 Button 的地方卻響應(yīng)的是點(diǎn)擊 Button 的事件。
  3. 內(nèi)存泄漏:使用屬性動(dòng)畫(huà)的時(shí)候卸勺,當(dāng)使用無(wú)限循環(huán)動(dòng)畫(huà)砂沛,需要在 Activity 退出的時(shí)候停止動(dòng)畫(huà),不然可能會(huì)因?yàn)闊o(wú)法釋放資源而導(dǎo)致 Activity 內(nèi)存泄漏孔庭。
  4. 動(dòng)畫(huà)兼容:當(dāng) APP 需要兼容到 API 11 以下的時(shí)候就需要注意動(dòng)畫(huà)的兼容問(wèn)題尺上。
  5. 使用 dp 而不是 px:因?yàn)?px 在不同設(shè)備上面的兼容問(wèn)題,使用動(dòng)畫(huà)的時(shí)候盡量使用 dp 作為單位圆到。
  6. 硬件加速:使用硬件加速可以提升動(dòng)畫(huà)的流暢性震檩。

源代碼

你可以在Github獲取以上程序的源代碼: Android-references

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锦亦,一起剝皮案震驚了整個(gè)濱河市择示,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挣菲,老刑警劉巖富稻,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異白胀,居然都是意外死亡椭赋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門或杠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哪怔,“玉大人,你說(shuō)我怎么就攤上這事向抢∪暇常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵挟鸠,是天一觀的道長(zhǎng)叉信。 經(jīng)常有香客問(wèn)我,道長(zhǎng)艘希,這世上最難降的妖魔是什么硼身? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮覆享,結(jié)果婚禮上佳遂,老公的妹妹穿的比我還像新娘。我一直安慰自己淹真,他們只是感情好讶迁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般巍糯。 火紅的嫁衣襯著肌膚如雪啸驯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天祟峦,我揣著相機(jī)與錄音罚斗,去河邊找鬼。 笑死宅楞,一個(gè)胖子當(dāng)著我的面吹牛针姿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厌衙,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼距淫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了婶希?” 一聲冷哼從身側(cè)響起榕暇,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喻杈,沒(méi)想到半個(gè)月后彤枢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筒饰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年缴啡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓷们。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡业栅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出换棚,到底是詐尸還是另有隱情式镐,我是刑警寧澤反镇,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布固蚤,位于F島的核電站,受9級(jí)特大地震影響歹茶,放射性物質(zhì)發(fā)生泄漏夕玩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一惊豺、第九天 我趴在偏房一處隱蔽的房頂上張望燎孟。 院中可真熱鬧,春花似錦尸昧、人聲如沸揩页。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爆侣。三九已至萍程,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兔仰,已是汗流浹背茫负。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乎赴,地道東北人忍法。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像榕吼,于是被迫代替她去往敵國(guó)和親饿序。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容