屬性動畫(Property Animation)學(xué)習(xí)心得

大致介紹

屬性動畫系統(tǒng)是一個允許你對幾乎任何事情進(jìn)行的動畫操作的健壯的框架。你可以定義一個屬性動畫來改變?nèi)魏螌ο蟮哪骋粚傩裕词惯@個對象不能夠在屏幕上顯示。一個屬性動畫在指定的時間內(nèi)改變對象的一個屬性值(成員變量),而你要做的是指定哪一個屬性被“動畫”,例如對象在屏幕上的位置,動畫持續(xù)多久,以及在動畫始末時刻的屬性值。

  • 持續(xù)時間:屬性動畫默認(rèn)的持續(xù)時間為300ms;
  • 時間插值器:指定在動畫持續(xù)時間內(nèi),屬性值以何種速率從起始值變化到默認(rèn)值;
  • 重復(fù)次數(shù)和行為:指定單此動畫執(zhí)行完畢的時候是否繼續(xù)正向重復(fù)執(zhí)行,或者反向執(zhí)行,以及動畫重復(fù)的字?jǐn)?shù);
  • 動畫集合:指定一個包含多個屬性進(jìn)行“動畫”的動畫組(Group);
  • 幀刷新時間:指定動畫執(zhí)行過程中,幀與幀之間的刷新時間,默認(rèn)值為10ms,但在真實應(yīng)用程序中,它取決于系統(tǒng)是否繁忙以及系統(tǒng)的處理速度。

屬性動畫與視圖動畫(View Animation)的區(qū)別

視圖動畫又稱為補(bǔ)間動畫(Tween),視圖動畫系統(tǒng)僅僅具備為視圖(View)對象提供動畫的能力,它對非視圖對象(non-View objects)無能為力。事實上,視圖動畫對于視圖對象而言也只在很少的方面起作用,例如大小、旋轉(zhuǎn)可行,但背景色就不起作用了。

視圖動畫的另一個不足就是它改變的只是視圖對象的“外表”,而非視圖對象本身。例如,通過視圖動畫讓一個視圖對象在屏幕上進(jìn)行移動,移動前位置為A,移動后位置為B,即移動后與移動前“不在”同一個位置,但該視圖對象的真實位置依然在A,B位置的內(nèi)容只是系統(tǒng)繪出(drawn)的“虛假”圖形,如果要操作這個視圖對象,必須實現(xiàn)自己的操作邏輯。假如使用屬性動畫操作上述視圖對象,它的位置就是真實的發(fā)生了變化。

屬性動畫無論對視圖對象還是非視圖對象都其作用,并且其作用范圍涵蓋顏色、位置、大小或者一切目標(biāo)對象定義的任何屬性。

然而,視圖動畫由于具備代碼量更少,耗時較短的特點,當(dāng)視圖動畫能夠滿足我們的需求的時候,不必使用屬性動畫。在不同的情景下選擇更高效的動畫系統(tǒng),彰顯出了兩種動畫系統(tǒng)(屬性與視圖)存在的意義。

相關(guān)API

第一類:

Animator:該類提供了創(chuàng)建屬性動畫的的基本結(jié)構(gòu),通常情況下不會直接使用該類,因為它提供了很少的方法來支持操作動畫屬性值。大部分情況下我們使用的是其子類。

ValueAnimator:繼承自Animator,它是主要的在動畫過程中,計算某一時刻屬性值的計算引擎。它具備所有的核心功能方法,包括每一個動畫的時序細(xì)節(jié),動畫是否重復(fù)的信息,監(jiān)聽到的更新時間,以及設(shè)置自定義屬性值類型計算器的能力。

屬性動畫的實現(xiàn)主要包括兩方便的內(nèi)容:一方面是計算不同時刻的屬性值,另一方面是將計算出來的屬性值賦值給目標(biāo)對象。然而ValueAnimator是不能夠?qū)崿F(xiàn)第二方面的,因此必須自己監(jiān)聽屬性值的更新并將其修改在目標(biāo)對象上。

ObjectAnimator:繼承自ValueAnimator,它允許我們在指定對象屬性的時候同時指定目標(biāo)對象,當(dāng)計算出一個對應(yīng)于某一時刻的新的屬性值時,該類會自動更新目標(biāo)對象的對應(yīng)屬性的屬性值。大部分時間我們都在使用ObjectAnimator,因為它讓新的屬性值作用于目標(biāo)對象的過程更為簡單。但是,我們有些時候不得不直接使用ValueAnimator,因為ObjectAnimator有一些限制:例如要求目標(biāo)對象必須具備一些特定的方法。

AnimatorSet:該類提供了多個屬性動畫“協(xié)調(diào)工作”的途徑,不同屬性動畫之間可以“同步執(zhí)行”,亦可以“依次執(zhí)行”,或者“延時執(zhí)行”。

第二類:

TypeEvaluator:接口,用于告訴系統(tǒng)將要改變的對象屬性是什么類型的,以及如何計算要改變的屬性。該接口的實現(xiàn)類依靠Animator提供的時序數(shù)據(jù)和屬性始末值,來計算動畫過程中各個時刻的屬性值。系統(tǒng)主要提供了如下實現(xiàn)類。

IntEvaluator:是系統(tǒng)提供的默認(rèn)的用于計算屬性值為int類型的計算器。

FloatEvaluator:是系統(tǒng)提供的默認(rèn)的用于計算屬性值為float類型的計算器。

ArgbEvaluator:系統(tǒng)提供的默認(rèn)的用于計算屬性值為十六進(jìn)制的色值類型的計算器。

當(dāng)然,如果你要操作的目標(biāo)對象的目標(biāo)屬性不在上述范圍內(nèi),你就需要(必須)實現(xiàn)TypeEvaluator接口來指定如何計算屬性的動畫值。除此之外,你也可以通過自定義實現(xiàn)TypeEvaluator接口,為int,float,argb(color)類型的屬性提供不同于系統(tǒng)默認(rèn)的處理行為。

第三類:

TimeInterpolator:時間插值器(接口),用于指定在動畫持續(xù)的過程中,屬性值在單位時間內(nèi)步進(jìn)的增量大小,也就是屬性值變化的速率,系統(tǒng)提供了如下實現(xiàn)類(以下列出的實現(xiàn)類只是常用的)。

LinearInterpolator:表明在動畫持續(xù)過程中,屬性值的變化速率是恒定的。

AccelerateInterpolator:表明在動畫持續(xù)過程中,屬性值的變化速率是一直增大的。

DecelerateInterpolator:表明在動畫持續(xù)過程中,屬性值的變化速率是一直減小的。

AccelerateDecelerateInterpolator:表明在動畫持續(xù)過程中,屬性值的變化速率是前半段一直增大,后半段持續(xù)減小的。

CycleInterpolator:為重復(fù)的屬性動畫指定了屬性值的變化速率按照正弦曲線改變。

當(dāng)然,如果系統(tǒng)提供的時間插值器不能夠滿足我們的需求,我們可以實現(xiàn)TimeInterpolator接口來構(gòu)造時間插值。

通過ValueAnimator實現(xiàn)屬性動畫

我們可以通過調(diào)用ValueAnimator的諸如ofInt(),ofFloat(),ofObject()等工廠方法來獲取ValueAnimator對象。

比如:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();

當(dāng)屬性值類型非int,float的時候,可以進(jìn)行如下操作:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

上述兩段代碼中,并不能真實的作用于任何一個對象,因為ValueAnimator不能夠直接操作對象或?qū)傩?。想要解決這個問題,我們可以在ValueAnimator定義一個接口來適時地處理諸如幀更新的時間,在實現(xiàn)這個接口的時候,我們可以通過調(diào)用getAnimatedValue()方法來獲得為每一幀計算出來的屬性值。

通過ObjectAnimator實現(xiàn)屬性動畫

ObjectAnimator整合了時序引擎和ValueAnimator的值計算能力,因此它可以完成對目標(biāo)對象的指定屬性的“屬性動畫 ”的操作。為了讓對任何對象都能夠更簡單的實現(xiàn)屬性動畫,我們不必再實現(xiàn)ValueAnimator.AnimatorUpdateListener接口,因為指定的屬性會自動更新。

初始化ObjectAnimator和ValueAnimator是相似的,不同的是初始化ObjectAnimator的時候,要指定目標(biāo)對象和該對象的屬性名稱(String類型),以及動畫始末該屬性的屬性值。例如:

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

為了確保指定屬性在動畫持續(xù)的過程中能夠自動更新,必須注意以下幾點:

  1. 目標(biāo)對象的目標(biāo)屬性必須具有setter方法,如果該屬性的setter方法不存在,可以:
  • 如果權(quán)限允許,增加setter方法;
  • 如果權(quán)限允許,寫一個包裹類(wrapper),該包裹類應(yīng)具有setter方法并指向原始目標(biāo)對象的目標(biāo)屬性;
  • 使用ValueAnimator代替。
  1. 如果在對ObjectAnimator的工廠方法傳實參的時候,values...的實參只傳了一個值,該值將是動畫結(jié)束的屬性值。為了系統(tǒng)能夠取到動畫開始的屬性值,目標(biāo)對象的目標(biāo)屬性必須具備getter方法。
  2. 在指定屬性動畫的始末屬性值的時候,始末屬性值的類型必須和getter方法(如果該方法是必需的)和setter方法中參數(shù)的類型相一致,比如:targetObject.setPropName(float) and targetObject.getPropName(float),當(dāng)構(gòu)造ObjectAnimator對象的時候,就必須ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  3. 當(dāng)操作的對象是視圖對象的“可見屬性”(顏色,大?。r,可能需要我們自己調(diào)用invalidate()強(qiáng)制使屏幕重繪對象本身。這步操作需要在onAnimationUpdate()回調(diào)方法中完成。視圖對象的所有setter方法,例如setAlpha()setTranslationX()都能夠使視圖對象實時的重繪,因此這些屬性在設(shè)置新值得時候不必考慮重繪的問題。

使用AnimatorSet組織多個屬性動畫

在許多時候,一個動畫的開始可能取決于另一個動畫的開始或結(jié)束。Android系統(tǒng)允許使用AnimatorSet組織不同的屬性動畫,以便我們可以指定不同動畫間的播放形式是同步、異步或是延時。當(dāng)然,也可以將一個AnimatorSet對象嵌入另一個AnimatorSet對象中。

例如下面代碼的實現(xiàn)效果就是依次:

  1. 播放bounceAnim動畫;

  2. 同時播放squashAnim1,squashAnim2,stretchAnim1,stretchAnim2四個動畫;

  3. 播放bounceBackAnim動畫;

  4. 播放fadeAnim動畫。

    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();

Animation Listeners

我們可以在動畫執(zhí)行的過程中監(jiān)聽重要的事件。

Animator.AnimatorListener

  • onAnimationStart(),動畫開始的時候調(diào)用;
  • onAnimationEnd(),動畫結(jié)束的時候調(diào)用;
  • onAnimationRepeat(),動畫重復(fù)的時候調(diào)用;
  • onAnimationCancel(),動畫被取消的時候調(diào)用,被取消的動畫也會調(diào)用onAnimationEnd()方法。

ValueAnimator.AnimatorUpdateListener

  • onAnimationUpdate(),動畫的每一幀會調(diào)用??梢栽诒O(jiān)聽這個事件的時候使用由ValueAnimator計算生成的屬性值。使用ValueAnimator的時候可以調(diào)用getAnimatedValue()方法來獲取新的屬性值。

對于第一個接口的使用,我們可以繼承AnimatorListenerAdapter類來代替實現(xiàn)Animator.AnimatorListener接口。AnimatorListenerAdapter類提供了所有需要實現(xiàn)的抽象方法的空實現(xiàn)。例如:

ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
  public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

為布局的改變添加屬性動畫

和能夠為視圖對象提供簡單的屬性動畫一樣,屬性動畫系統(tǒng)也有能力為ViewGroup對象提供動畫。

當(dāng)在一個ViewGroup中的布局發(fā)生變化的時候,可以使用LayoutTransition類為其添加動畫。這里所指的布局變化是指ViewGroup中的View對象隱藏和出現(xiàn)。隱藏可由兩種情況導(dǎo)致:ViewGroup的removeView(),以及view的setVisibility(INVISIBLE/GONE);出現(xiàn)可由兩種情況導(dǎo)致:ViewGroup的addView(),以及view的setVisibility(VISIBLE)。從你開始調(diào)用addView或者removeView,到控件到達(dá)他們應(yīng)該到達(dá)的新位置期間,你可以定義這段時間的動畫。

通過調(diào)用LayoutTransition對象的setAnimator()方法,傳入一個Animator對象和不同的常量可以設(shè)置四種不同的動畫:

  • 常量1:LayoutTransition.APPEARING ViewGroup中有View出現(xiàn)的時候,View的動畫;
  • 常量2:LayoutTransition.CHANGE_APPEARING ViewGroup中有View出現(xiàn)的時候,ViewGroup的動畫;
  • 常量3:LayoutTransition.DISAPPEARING ViewGroup中有View消失的時候,View的動畫;
  • 常量4:LayoutTransition.CHANGE_DISAPPEARING ViewGroup中有View消失的時候,ViewGroup的動畫;

可以自定義我們自己的動畫效果,方法如下:

viewGroup.setLayoutTransition(mTransitioner);

當(dāng)然也可以使用系統(tǒng)提供的默認(rèn)動畫效果,方法是:在布局文件中將目標(biāo)ViewGroup的android:animateLayoutchanges屬性值設(shè)置為true

使用接口TypeEvaluator

如果目標(biāo)對象的目標(biāo)屬性的屬性值類型是系統(tǒng)未提供的,你可以通過實現(xiàn)TypeEvaluator接口來定義自己的計算器(Evaluator)。系統(tǒng)提供的類型有int、float以及顏色值,分別對應(yīng)IntEvaluator, FloatEvaluator和ArgbEvaluator。

實現(xiàn)該接口僅僅需要實現(xiàn)一個evaluate()方法,該方法需要返回一個在動畫過程中的某一時刻合適的目標(biāo)屬性值。例如FloatEvaluator的實現(xiàn)過程為:

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);
     }
}

注意:當(dāng)ValueAnimator或ObjectAnimator運(yùn)行的時候,它計算從動畫開始,到當(dāng)前時刻,已經(jīng)過去的部分(介于0-1之間的數(shù));然后計算我們使用(傳入)的插值器版本。插值器部分的內(nèi)容就是TypeEvaluator中接收到的fraction參數(shù),因此在計算目標(biāo)屬性某一個的屬性值的時候不必再考慮插值器的影響。

使用插值器

一個插值器作為一個時間的函數(shù),定義了如何影響動畫過程中目標(biāo)屬性的屬性值。例如,可以指定動畫線性的發(fā)生,意味著在整個時間內(nèi)均勻的移動卡給你;當(dāng)然也可以使用非線性時間,比如開始加速,末尾減速。

動畫系統(tǒng)中的插值器接收一個來自Animators(代表動畫已經(jīng)過去的時間)的分?jǐn)?shù)。插值器修改這個分?jǐn)?shù),使結(jié)果一致于預(yù)期的目標(biāo)。系統(tǒng)在android.view.animation包下提供了一系列公共的插值器,如果這些都不適合我們,可以實現(xiàn)TimeInterpolator接口來創(chuàng)造屬于自己的插值器。

AccelerateDecelerateInterpolator的實現(xiàn)方式:

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator的實現(xiàn)方式:

public float getInterpolation(float input) {
    return input;
}

指定Keyframes

一個Keyframe對象包含一對“時間/值”對,這個“時間/值”對用于指定屬性動畫在特定時間的條件下的特定狀態(tài)。每一個Keyframe可以有屬于它本身的插值器,用來控制動畫從上一個Keyframe的時間值到這一個Keyframe對象的時間值之間的動畫的行為。

可以通過調(diào)用ofInt(), ofFloat(), 或ofObject()工廠方法來獲得Keyframe對象;可以通過調(diào)用ofKeyframe()工廠方法來獲得PropertyValuesHolder對象,一旦有了PropertyValuesHolder對象,就可以通過將其和目標(biāo)對象作為參數(shù)獲得動畫對象,下面是示例代碼:

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);

對于視圖對象的優(yōu)化

屬性動畫系統(tǒng)優(yōu)化了一些針對目標(biāo)對象是視圖對象的方法。我們知道,視圖動畫(補(bǔ)間動畫)系統(tǒng)通過改變繪制視圖方法的方法改造視圖對象,這在每一個視圖對象所在的容器(container:ViewGroup)中被處理,因為視圖對象本身并沒有屬性可以被修改。結(jié)果是視圖開始了動畫,但視圖對象本身并沒有發(fā)生變化(移動的是幻影,本身沒有動)。從Android 3.0開始,添加了新的屬性和相應(yīng)的setter、getter方法以避免上述漏洞。

屬性動畫可以通過改變真實的屬性值在屏幕上操作視圖對象。除此之外,視圖對象黨旗屬性發(fā)生改變的時候可以自動調(diào)用invalidate()方法來刷新屏幕進(jìn)行重繪。在View類中助于屬性動畫的新屬性有:

  • translationX和translationY:距左,距上的平移;
  • rotation, rotationX, 和rotationY:2D(rotation)和3D上的旋轉(zhuǎn);
  • scaleX和scaleY:2D上的縮放;
  • pivotX和pivotY:參考位置,圍繞該點進(jìn)行縮放或旋轉(zhuǎn)。默認(rèn)是對象的中心;
  • x和y:簡單實用的用來描述在容器中的最終位置的屬性;
  • alpha:透明度

示例代碼:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

通過ViewPropertyAnimator實現(xiàn)屬性動畫

ViewPropertyAnimator提供了一個簡單的方法,僅用一個Animator對象來并行處理View對象的少量的屬性。它的作用很像ObjectAnimator,因為它修改了視圖對象的真實屬性值,且更加高效。除此之外,使用ViewPropertyAnimator的代碼更加簡明易懂。下面是不同方法的對比,效果是同時修改視圖對象的x和y屬性。

多個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();

一個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);

在XML文件中聲明屬性動畫

屬性動畫系統(tǒng)允許在xml文件中聲明屬性動畫,在xml文件中定義屬性動畫,更以更加方便的在多個activity中復(fù)用,并可以更加簡單的調(diào)整動畫的順序。

屬性動畫的xml文件應(yīng)保存在res/animator/文件夾下,下面是聲明屬性動畫類的的標(biāo)簽:

  • ValueAnimator: <animator>
  • ObjectAnimator:<objectAnimator>
  • AnimatorSet:<set>

示例代碼如下:

<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>

為了使用這個屬性動畫,必須使用AnimatorSet對象來填充xml資源,然后設(shè)置目標(biāo)對象,實例代碼如下:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
set.setTarget(myObject);
set.start();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容