android屬性動(dòng)畫詳解

一、概述

Android動(dòng)畫中,總共有兩種類型的動(dòng)畫View Animation(視圖動(dòng)畫)和Property Animator(屬性動(dòng)畫);其中
View Animation包括Tween Animation(補(bǔ)間動(dòng)畫)和Frame Animation(逐幀動(dòng)畫);
Property Animator包括ValueAnimator和ObjectAnimation;
首先,直觀上,他們有如下三點(diǎn)不同:
1、引入時(shí)間不同:View Animation是API Level 1就引入的。Property Animation是API Level 11引入的,即Android 3.0才開(kāi)始有Property Animation相關(guān)的API。
2、所在包名不同:View Animation在包android.view.animation中。而Property Animation API在包 android.animation中。
3、動(dòng)畫類的命名不同:View Animation中動(dòng)畫類取名都叫XXXXAnimation,而在Property Animator中動(dòng)畫類的取名則叫XXXXAnimator大家都知道逐幀動(dòng)畫主要是用來(lái)實(shí)現(xiàn)動(dòng)畫的,而補(bǔ)間動(dòng)畫才能實(shí)現(xiàn)控件的漸入漸出、移動(dòng)、旋轉(zhuǎn)和縮放的;而Property Animator是在Android 3.0版本才引入的,之前是沒(méi)有的。
為什么還要引入Property Animator呢?
1、為什么引入Property Animator(屬性動(dòng)畫)
我提出一個(gè)假設(shè):請(qǐng)問(wèn)大家,如何利用補(bǔ)間動(dòng)畫來(lái)將一個(gè)控件的背景色在一分鐘內(nèi)從綠色變?yōu)榧t色?這個(gè)效果想必沒(méi)辦法僅僅通過(guò)改變控件的漸入漸出、移動(dòng)、旋轉(zhuǎn)和縮放來(lái)實(shí)現(xiàn)吧,而這個(gè)效果是可以通過(guò)Property Animator完美實(shí)現(xiàn)的
**這就是第一個(gè)原因:Property Animator能實(shí)現(xiàn)補(bǔ)間動(dòng)畫無(wú)法實(shí)現(xiàn)的功能 **大家都知道,補(bǔ)間動(dòng)畫和逐幀動(dòng)畫統(tǒng)稱為View Animation,也就是說(shuō)這兩個(gè)動(dòng)畫只能對(duì)派生自View的控件實(shí)例起作用;而Property Animator則不同,從名字中可以看出屬性動(dòng)畫,應(yīng)該是作用于控件屬性的!正因?yàn)閷傩詣?dòng)畫能夠只針對(duì)控件的某一個(gè)屬性來(lái)做動(dòng)畫,所以也就造就了他能單獨(dú)改變控件的某一個(gè)屬性的值!比如顏色!這就是Property Animator能實(shí)現(xiàn)補(bǔ)間動(dòng)畫無(wú)法實(shí)現(xiàn)的功能的最重要原因。
**我們得到了第二點(diǎn)不同:View Animation僅能對(duì)指定的控件做動(dòng)畫,而Property Animator是通過(guò)改變控件某一屬性值來(lái)做動(dòng)畫的。
**假設(shè)我們將一個(gè)按鈕從左上角利用補(bǔ)間動(dòng)畫將其移動(dòng)到右下角,在移動(dòng)過(guò)程中和移動(dòng)后,這個(gè)按鈕都是不會(huì)響應(yīng)點(diǎn)擊事件的。這是為什么呢?因?yàn)檠a(bǔ)間動(dòng)畫僅僅轉(zhuǎn)變的是控件的顯示位置而已,并沒(méi)有改變控件本身的值。View Animation的動(dòng)畫實(shí)現(xiàn)是通過(guò)其Parent View實(shí)現(xiàn)的,在View被drawn時(shí)Parents View改變它的繪制參數(shù),這樣雖然View的大小或旋轉(zhuǎn)角度等改變了,但View的實(shí)際屬性沒(méi)變,所以有效區(qū)域還是應(yīng)用動(dòng)畫之前的區(qū)域;我們看到的效果僅僅是系統(tǒng)作用在按鈕上的顯示效果,利用動(dòng)畫把按鈕從原來(lái)的位置移到了右下角,但按鈕內(nèi)部的任何值是沒(méi)有變化的,所以按鈕所捕捉的點(diǎn)擊區(qū)域仍是原來(lái)的點(diǎn)擊區(qū)域。(下面會(huì)舉例來(lái)說(shuō)明這個(gè)問(wèn)題)
這就得到了第三點(diǎn)不同:補(bǔ)間動(dòng)畫雖能對(duì)控件做動(dòng)畫,但并沒(méi)有改變控件內(nèi)部的屬性值。而Property Animator則是恰恰相反,Property Animator是通過(guò)改變控件內(nèi)部的屬性值來(lái)達(dá)到動(dòng)畫效果的

二、ValueAnimator簡(jiǎn)單使用

我們前面講了Property Animator包括ValueAnimator和ObjectAnimator;這篇文章就主要來(lái)看看ValueAnimator的使用方法吧。
我覺(jué)得谷歌那幫老頭是最會(huì)起名字的人,單從命名上,就能看出來(lái)這個(gè)東東的含義。ValueAnimator從名字可以看出,這個(gè)Animation是針對(duì)值的!ValueAnimator不會(huì)對(duì)控件做任何操作,我們可以給它設(shè)定從哪個(gè)值運(yùn)動(dòng)到哪個(gè)值,通過(guò)監(jiān)聽(tīng)這些值的漸變過(guò)程來(lái)自己操作控件。它會(huì)自己計(jì)算動(dòng)畫的過(guò)程,然后我們需要監(jiān)聽(tīng)它的動(dòng)畫過(guò)程來(lái)自己操作控件。

    1. 第一步:創(chuàng)建ValueAnimator實(shí)例
ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  
animator.start();  
  1. 在這里我們利用ValueAnimator.ofInt創(chuàng)建了一個(gè)值從0到400的動(dòng)畫,動(dòng)畫時(shí)長(zhǎng)是1s,然后讓動(dòng)畫開(kāi)始。從這段代碼中可以看出,ValueAnimator沒(méi)有跟任何的控件相關(guān)聯(lián),那也正好說(shuō)明ValueAnimator只是對(duì)值做動(dòng)畫運(yùn)算,而不是針對(duì)控件的,我們需要監(jiān)聽(tīng)ValueAnimator的動(dòng)畫過(guò)程來(lái)自己對(duì)控件做操作。 第二步:添加監(jiān)聽(tīng)
    上面的三行代碼,我們已經(jīng)實(shí)現(xiàn)了動(dòng)畫,下面我們就添加監(jiān)聽(tīng):
ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        Log.d("qijian","curValue:"+curValue);  
    }  
});  
animator.start();

這就是ValueAnimator的功能:ValueAnimator對(duì)指定值區(qū)間做動(dòng)畫運(yùn)算,我們通過(guò)對(duì)運(yùn)算過(guò)程做監(jiān)聽(tīng)來(lái)自己操作控件。
總而言之就是兩點(diǎn):
1. ValueAnimator只負(fù)責(zé)對(duì)指定的數(shù)字區(qū)間進(jìn)行動(dòng)畫運(yùn)算
2. 我們需要對(duì)運(yùn)算過(guò)程進(jìn)行監(jiān)聽(tīng),然后自己對(duì)控件做動(dòng)畫操作

三、插值器Interpolator

插值器的意義其實(shí)就相當(dāng)于物理公式中的加速度參數(shù),所以這也就是它也叫加速器的原因。 如何自定義插值器:

public class LinearInterpolator implements Interpolator {  
  
    public LinearInterpolator() {  
    }  
  
    public LinearInterpolator(Context context, AttributeSet attrs) {  
    }  
  
    public float getInterpolation(float input) {  
        return input;  
    }  
}  
public interface Interpolator extends TimeInterpolator {  
} 
  1. getInterpolation(float input):
    參數(shù)input:input參數(shù)是一個(gè)float類型,它取值范圍是0到1,表示當(dāng)前動(dòng)畫的進(jìn)度,取0時(shí)表示動(dòng)畫剛開(kāi)始,取1時(shí)表示動(dòng)畫結(jié)束,取0.5時(shí)表示動(dòng)畫中間的位置,其它類推。
    返回值:表示當(dāng)前實(shí)際想要顯示的進(jìn)度。取值可以超過(guò)1也可以小于0,超過(guò)1表示已經(jīng)超過(guò)目標(biāo)值,小于0表示小于開(kāi)始位置。
    對(duì)于input參數(shù),它表示的是當(dāng)前動(dòng)畫的進(jìn)度,勻速增加的。動(dòng)畫的進(jìn)度就是動(dòng)畫在時(shí)間上的進(jìn)度,與我們的任何設(shè)置無(wú)關(guān),隨著時(shí)間的增長(zhǎng),動(dòng)畫的進(jìn)度自然的增加,從0到1;input參數(shù)相當(dāng)于時(shí)間的概念,通過(guò)setDuration()指定了動(dòng)畫的時(shí)長(zhǎng),在這個(gè)時(shí)間范圍內(nèi),動(dòng)畫進(jìn)度肯定是一點(diǎn)點(diǎn)增加的;就相當(dāng)于播放一首歌,這首歌的進(jìn)度是從0到1是一樣的。 而返回值則表示動(dòng)畫的數(shù)值進(jìn)度,它的對(duì)應(yīng)的數(shù)值范圍是我們通過(guò)ofInt(),ofFloat()來(lái)指定的,這個(gè)返回值就表示當(dāng)前時(shí)間所對(duì)應(yīng)的數(shù)值的進(jìn)度。
/** 
 * A time interpolator defines the rate of change of an animation. This allows animations 
 * to have non-linear motion, such as acceleration and deceleration. 
 */  
public interface TimeInterpolator {  
  
    /** 
     * Maps a value representing the elapsed fraction of an animation to a value that represents 
     * the interpolated fraction. This interpolated value is then multiplied by the change in 
     * value of an animation to derive the animated value at the current elapsed animation time. 
     * 
     * @param input A value between 0 and 1.0 indicating our current point 
     *        in the animation where 0 represents the start and 1.0 represents 
     *        the end 
     * @return The interpolation value. This value can be more than 1.0 for 
     *         interpolators which overshoot their targets, or less than 0 for 
     *         interpolators that undershoot their targets. 
     */  
    float getInterpolation(float input);  
}  

**input參數(shù)與任何我們?cè)O(shè)定的值沒(méi)關(guān)系,只與時(shí)間有關(guān),隨著時(shí)間的增長(zhǎng),動(dòng)畫的進(jìn)度也自然的增加,input參數(shù)就代表了當(dāng)前動(dòng)畫的進(jìn)度。而返回值則表示動(dòng)畫的當(dāng)前數(shù)值進(jìn)度 **

public class LinearInterpolator implements Interpolator {  
  
    …………  
  
    public float getInterpolation(float input) {  
       return 1-input; 
    }  
} 

在getInterpolation函數(shù)中,我們將進(jìn)度反轉(zhuǎn)過(guò)來(lái),當(dāng)傳0的時(shí)候,我們讓它數(shù)值進(jìn)度在完成的位置,當(dāng)完成的時(shí)候,我們讓它在開(kāi)始的位置

四、ObjectAnimator

ObjectAnimator是派生自ValueAnimator的,所以ValueAnimator中所能使用的方法,在ObjectAnimator中都可以正常使用。ObjectAnimator重寫了幾個(gè)方法,比如ofInt(),ofFloat()等。利用ObjectAnimator重寫的ofFloat方法如何實(shí)現(xiàn)一個(gè)動(dòng)畫:(改變透明度)

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);  
animator.setDuration(2000);  
animator.start(); 
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
  1. 第一個(gè)參數(shù)用于指定這個(gè)動(dòng)畫要操作的是哪個(gè)控件
  2. 第二個(gè)參數(shù)用于指定這個(gè)動(dòng)畫要操作這個(gè)控件的哪個(gè)屬性
  3. 第三個(gè)參數(shù)是可變長(zhǎng)參數(shù),這個(gè)就跟ValueAnimator中的可變長(zhǎng)參數(shù)的意義一樣了,就是指這個(gè)屬性值是從哪變到哪。像我們上面的代碼中指定的就是將textview的alpha屬性從0變到1再變到0;
    TextView控件有rotation這個(gè)屬性嗎?沒(méi)有,不光TextView沒(méi)有,連它的父類View中也沒(méi)有這個(gè)屬性。那它是怎么來(lái)改變這個(gè)值的呢?其實(shí),ObjectAnimator做動(dòng)畫,并不是根據(jù)控件xml中的屬性來(lái)改變的,而是通過(guò)指定屬性所對(duì)應(yīng)的set方法來(lái)改變的。比如,我們上面指定的改變r(jià)otation的屬性值,ObjectAnimator在做動(dòng)畫時(shí)就會(huì)到指定控件(TextView)中去找對(duì)應(yīng)的setRotation()方法來(lái)改變控件中對(duì)應(yīng)的值。同樣的道理,當(dāng)我們?cè)谧铋_(kāi)始的示例代碼中,指定改變”alpha”屬性值的時(shí)候,ObjectAnimator也會(huì)到TextView中去找對(duì)應(yīng)的setAlpha()方法。那TextView中都有這些方法嗎,有的,這些方法都是從View中繼承過(guò)來(lái)的,在View中有關(guān)動(dòng)畫,總共有下面幾組set方法:
public void setAlpha(float alpha)  
//2、旋轉(zhuǎn)度數(shù):rotation、rotationX、rotationY  
public void setRotation(float rotation)  
public void setRotationX(float rotationX)  
public void setRotationY(float rotationY)  
  //3、平移:translationX、translationY  
public void setTranslationX(float translationX)   
public void setTranslationY(float translationY)  
  //縮放:scaleX、scaleY  
public void setScaleX(float scaleX)  
public void setScaleY(float scaleY) 
依據(jù)這點(diǎn),自定義動(dòng)畫屬性:
public class MyPointView extends View {  
    private Point mPoint = new Point(100);  
    public MyPointView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
    @Override  
    protected void onDraw(Canvas canvas) {  
        if (mPoint != null){  
            Paint paint = new Paint();  
            paint.setAntiAlias(true);  
            paint.setColor(Color.RED);  
            paint.setStyle(Paint.Style.FILL);  
            canvas.drawCircle(300,300,mPoint.getRadius(),paint);  
        }  
        super.onDraw(canvas);  
    }  
    //第一點(diǎn),這個(gè)set函數(shù)所對(duì)應(yīng)的屬性應(yīng)該是pointRadius或者PointRadius。前面我們已經(jīng)講了第一個(gè)字母大小寫無(wú)所謂,后面的字母必須保持與set函數(shù)完全一致。 
    //第二點(diǎn),在setPointRadius中,先將當(dāng)前動(dòng)畫傳過(guò)來(lái)的值保存到mPoint中,做為當(dāng)前圓形的半徑。然后強(qiáng)制界面刷新 
    void setPointRadius(int radius){  
        mPoint.setRadius(radius);  
        invalidate();  
    }    
} 
private void doPointViewAnimation(){  
     ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius", 0, 300, 100);  
      animator.setDuration(2000);  
      animator.start();  
}  

前面我們都是定義多個(gè)值,即至少兩個(gè)值之間的變化,那如果我們只定義一個(gè)值呢,如下面的方式:(同樣以MyPointView為例)

ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);  
animator.setDuration(2000);  
animator.start();  

僅且僅當(dāng)我們只給動(dòng)畫設(shè)置一個(gè)值時(shí),程序才會(huì)調(diào)用屬性對(duì)應(yīng)的get函數(shù)來(lái)得到動(dòng)畫初始值。如果動(dòng)畫沒(méi)有初始值,那么就會(huì)使用系統(tǒng)默認(rèn)值。比如ofInt()中使用的參數(shù)類型是int類型的,而系統(tǒng)的Int值的默認(rèn)值是0,所以動(dòng)畫就會(huì)從0運(yùn)動(dòng)到100;也就是系統(tǒng)雖然在找到不到屬性對(duì)應(yīng)的get函數(shù)時(shí),會(huì)給出警告,但同時(shí)會(huì)用系統(tǒng)默認(rèn)值做為動(dòng)畫初始值。
如果通過(guò)給自定義控件MyPointView設(shè)置了get函數(shù),那么將會(huì)以get函數(shù)的返回值做為初始值。

ArgbEvaluator

public class ArgbEvaluator implements TypeEvaluator {  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        int startInt = (Integer) startValue;  
        int startA = (startInt >> 24);  
        int startR = (startInt >> 16) & 0xff;  
        int startG = (startInt >> 8) & 0xff;  
        int startB = startInt & 0xff;  
  
        int endInt = (Integer) endValue;  
        int endA = (endInt >> 24);  
        int endR = (endInt >> 16) & 0xff;  
        int endG = (endInt >> 8) & 0xff;  
        int endB = endInt & 0xff;  
  
        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |  
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |  
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |  
                (int)((startB + (int)(fraction * (endB - startB))));  
    }  
}

根據(jù)View setBackGroundColor()方法可以自定義條用屬性動(dòng)畫。

ObjectAnimator animator = ObjectAnimator.ofInt(tv, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);  
animator.setDuration(8000);  
animator.setEvaluator(new ArgbEvaluator());  
animator.start();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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