Android動(dòng)畫詳解

Android應(yīng)用中經(jīng)常需要使用到各式各樣的動(dòng)畫效果凉袱,產(chǎn)品設(shè)計(jì)總是已他們重破天機(jī)的想象給出看著就很難實(shí)現(xiàn)的動(dòng)畫张足,不過(guò)值得慶幸的是Android的動(dòng)畫框架也在不斷地進(jìn)化以滿足我們的需求器瘪。Android發(fā)展到現(xiàn)在绅项,有多少種可以使用的動(dòng)畫類型呢赁咙,在本篇文章中將給出一些例子來(lái)給出答案今野。


在Android3.0之前的牙寞,我們可以使用的動(dòng)畫類型僅僅有兩種饺鹃,只能滿足一些簡(jiǎn)單的功能效果,他們分別是逐幀動(dòng)畫和補(bǔ)間動(dòng)畫碎税,而在Android3.0時(shí)尤慰,Android SDK為開發(fā)者帶來(lái)了強(qiáng)大而又靈活的屬性動(dòng)畫Property Animation(聽(tīng)上去就好高大上啊)雷蹂,可以稱之為動(dòng)畫界的重大突破伟端,它可以只通過(guò)簡(jiǎn)單的api調(diào)用就實(shí)現(xiàn)較為復(fù)雜的動(dòng)畫效果。而隨著時(shí)間的推進(jìn)啊匪煌,在Android4.4時(shí)责蝠,又為開發(fā)者帶來(lái)了android.transtion框架(基本常用的動(dòng)畫效果都給你封裝好了,你就偷著樂(lè)吧)萎庭,我們通過(guò)框架可以更直觀的定義動(dòng)畫效果霜医,接下來(lái)我們簡(jiǎn)單介紹一下除屬性動(dòng)畫之外的三種動(dòng)畫類型(因?yàn)閷傩詣?dòng)畫比較牛逼,一定要重點(diǎn)講解驳规,目前在一些自定義View中肴敛,使用一些動(dòng)畫效果都是使用屬性動(dòng)畫)。


裝逼ing

1.1 逐幀動(dòng)畫(Frame Animation)
逐幀動(dòng)畫也叫Drawable Animation吗购,是最簡(jiǎn)單最基本的動(dòng)畫類型了医男,他就是利用人眼的視覺(jué)暫留效應(yīng)——也就是光對(duì)視網(wǎng)膜所產(chǎn)生的視覺(jué)在光停止作用后也會(huì)保留一段時(shí)間的現(xiàn)象。那意思就是說(shuō)捻勉,逐幀動(dòng)畫就是一幀一幀的圖像咯镀梭!是的沒(méi)錯(cuò),設(shè)計(jì)師給出一系列狀態(tài)不斷變化的圖片踱启,開發(fā)者可以制定動(dòng)畫中每一幀對(duì)于的圖片和持續(xù)的時(shí)間报账,然后就可以播放幀動(dòng)畫了研底。下面我們通過(guò)xml的代碼的形式分別實(shí)現(xiàn)類似物流配送時(shí)的動(dòng)畫效果:
![image.png](http://upload-images.jianshu.io/upload_images/5815336- 5f61db40219e3da9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

圖片資源放一下:


a.png

b.png

c.png

d.png

1.1.1:xml方式實(shí)現(xiàn)逐幀動(dòng)畫
這是實(shí)現(xiàn)逐幀動(dòng)畫最常用的方式,首先我們將每一幀的圖片放入drawable目錄下透罢,然后在res/drawable目錄中新建一個(gè)xml文件榜晦,在這里文件中根目錄使用<animation-list>標(biāo)簽來(lái)定義幀動(dòng)畫執(zhí)行順序,在其中不僅可以指定每一幀的圖片琐凭,還可以指定動(dòng)畫持續(xù)時(shí)間等芽隆。具體如下:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">

<!--oneshot是否重復(fù)執(zhí)行動(dòng)畫,false表示只執(zhí)行一次,true表示執(zhí)行
    item幀動(dòng)畫順序统屈,drawable幀圖片胚吁,duration動(dòng)畫持續(xù)時(shí)長(zhǎng)-->

<item android:drawable="@drawable/a" android:duration="150"></item>
<item android:drawable="@drawable/b" android:duration="150"></item>
<item android:drawable="@drawable/c" android:duration="150"></item>
<item android:drawable="@drawable/d" android:duration="150"></item>


</animation-list>

之后我們只需要在xml中創(chuàng)建一個(gè)ImageView控件,設(shè)置其背景為剛才drawable目錄下定義的xml文件:
··

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.xjf.androidanimation.MainActivity">

<ImageView
    android:id="@+id/iv"
    android:layout_width="60dp"
    android:layout_height="40dp"
    android:background="@drawable/frame_anim"
    android:layout_gravity="center" />

</LinearLayout>

最后記得在Java中獲取到AnimationDrawable對(duì)象愁憔,調(diào)用start方法開始執(zhí)行動(dòng)畫:

  ImageView iv = findViewById(R.id.iv);
    AnimationDrawable animationDrawable = (AnimationDrawable) iv.getBackground();
    animationDrawable.start();

1.1.2:代碼方式:

   ImageView imageView = findViewById(R.id.iv);
    AnimationDrawable animationDrawable = new AnimationDrawable();
    Drawable a = getResources().getDrawable(R.drawable.a);
    Drawable b = getResources().getDrawable(R.drawable.b);
    Drawable c = getResources().getDrawable(R.drawable.c);
    Drawable d = getResources().getDrawable(R.drawable.d);
    animationDrawable.addFrame(a,120);
    animationDrawable.addFrame(b,120);
    animationDrawable.addFrame(c,120);
    animationDrawable.addFrame(d,120);

    imageView.setBackgroundDrawable(animationDrawable);
    animationDrawable.setOneShot(false);
    //動(dòng)畫開始
    animationDrawable.start();
    //停止
    animationDrawable.stop();

1.2:補(bǔ)間動(dòng)畫(Tween Animation)
補(bǔ)間動(dòng)畫不需要用戶定義動(dòng)畫啊執(zhí)行過(guò)程的每一幀腕扶,只需要定義動(dòng)畫開始和結(jié)束這兩個(gè)關(guān)鍵幀抡谐,并指定動(dòng)畫的變化時(shí)間和方式即可丰包,之后交給Android系統(tǒng)進(jìn)行計(jì)算镀岛,通過(guò)在兩個(gè)幀之間插入漸變值來(lái)實(shí)現(xiàn)平滑過(guò)渡苞轿,從而對(duì)View的內(nèi)容完成一系列的圖形變換來(lái)實(shí)現(xiàn)動(dòng)畫效果,它主要包括四種動(dòng)畫:透明度變化Alpha袍冷,大小變化Scale念秧,位移變化Translate及旋轉(zhuǎn)變化Rotate鸽心,這四種動(dòng)畫也可以組合秋茫,從而生成更加炫酷的效果史简,同樣定義補(bǔ)間動(dòng)畫也有XML和代碼兩種方式可以實(shí)現(xiàn),在了解動(dòng)畫之前肛著,我們有必要搞清楚插值器Interpolator圆兵。

1.2.1 插值器Interpolator
上一段落有簡(jiǎn)單提到Android系統(tǒng)會(huì)在補(bǔ)間動(dòng)畫的開始和結(jié)束幀直接插入漸變值,它依據(jù)的便是Interpolator枢贿,具體來(lái)說(shuō)殉农,Interpolator會(huì)根據(jù)類型的不同,選擇不同的算法計(jì)算出在補(bǔ)間動(dòng)畫期間所需要?jiǎng)討B(tài)插入幀的密度和位置局荚,Interpolator負(fù)責(zé)控制動(dòng)畫的變化速度超凳,使得前面所說(shuō)的四種基本動(dòng)畫能夠以勻速、加速耀态、減速轮傍、拋物線等多種效果進(jìn)行變化。

具體到Android代碼中茫陆,你可以發(fā)現(xiàn)Interpolator類是一個(gè)空接口金麸,它繼承自TimeInterpolator擎析,TimeInterpolator時(shí)間插值器允許動(dòng)畫進(jìn)行非線性運(yùn)動(dòng)變換簿盅,如加速或減速等挥下,該接口中世紀(jì)中有一個(gè)方法,入?yún)⑹?.0-1.0的一個(gè)值桨醋,返回值可以小于0也可以大于1棚瘟。

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

AndroidSDK默認(rèn)提供了幾個(gè)Interpolator的實(shí)現(xiàn)類來(lái)滿足我們的基本需求:


image.png

意義如下:

AccelerateDecelerateInterpolator 在動(dòng)畫開始與介紹的地方速率改變比較慢,在中間的時(shí)候加速
AccelerateInterpolator 在動(dòng)畫開始的地方速率改變比較慢喜最,然后開始加速
AnticipateInterpolator 開始的時(shí)候向后然后向前甩
AnticipateOvershootInterpolator 開始的時(shí)候向后然后向前甩一定值后返回最后的值
BounceInterpolator 動(dòng)畫結(jié)束的時(shí)候彈起
CycleInterpolator 動(dòng)畫循環(huán)播放特定的次數(shù)偎蘸,速率改變沿著正弦曲線
DecelerateInterpolator 在動(dòng)畫開始的地方快然后慢
LinearInterpolator 以常量速率改變
OvershootInterpolator 向前甩一定值后再回到原來(lái)位置

如果上述的默認(rèn)插值器不符合我們項(xiàng)目的實(shí)際需求,開發(fā)者也可以通過(guò)實(shí)現(xiàn)Interpolator接口來(lái)編寫自己的插值器瞬内。

好了迷雪,關(guān)于插值器的介紹就到這吧,具體的效果朋友們可以在自己編寫的Tween Animation中加入不同的插值器去了解虫蝶。

1.3 補(bǔ)間動(dòng)畫Tween Animation具體實(shí)現(xiàn):
首先我們看一下章咧,系統(tǒng)提供了哪些:


1-3-1.png

然后了解一下他們各自的效果(其實(shí)就是我們上面所講的四種動(dòng)畫類型了):


1.--2.png

1.3.1:AlphaAnimation(透明度動(dòng)畫效果)

xml方式定義:在res/anim(沒(méi)有anim就自己新建一個(gè))中新建一個(gè)xml文件,根元素為set:


1-3-3
<?xml version="1.0" encoding="utf-8"?>
  <set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
>
<translate
    android:fromYDelta="100%p"
    android:toYDelta="0"></translate>

<alpha android:fromAlpha="0.0"
    android:toAlpha="1.0"></alpha>
</set>

在Java代碼中:

  //設(shè)置透明度變化為0-1
    AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
    //設(shè)置動(dòng)畫時(shí)長(zhǎng)
    alphaAnimation.setDuration(200);
   //創(chuàng)建插值器
    AccelerateDecelerateInterpolator interpolator = new         AccelerateDecelerateInterpolator();
    //插值器和動(dòng)畫綁定
    alphaAnimation.setInterpolator(interpolator);
    //設(shè)置動(dòng)畫結(jié)束后保留結(jié)束狀態(tài)
    alphaAnimation.setFillAfter(true);
    imageView.setAnimation(alphaAnimation);

AlphaAnimation動(dòng)畫的構(gòu)造方法只有一個(gè):(參數(shù)分別是初始的透明度和結(jié)束的透明度)

 /**
 * Constructor to use when building an AlphaAnimation from code
 * 
 * @param fromAlpha Starting alpha value for the animation, where 1.0 means
 *        fully opaque and 0.0 means fully transparent.
 * @param toAlpha Ending alpha value for the animation.
 */
public AlphaAnimation(float fromAlpha, float toAlpha) {
    mFromAlpha = fromAlpha;
    mToAlpha = toAlpha; }

1.3.2:ScaleAnimation
xml方式:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="200">

<scale
    android:fromXScale="0.2"
    android:fromYScale="0.2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1.5"
    android:toYScale="1.5"></scale>
</set>

Java代碼:

   ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f,4.0f,1.0f,4.0f, Animation.RELATIVE_TO_SELF,0.0f,Animation.RELATIVE_TO_SELF,0.0f);
    scaleAnimation.setDuration(200);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

ScaleAnimation的構(gòu)造方法就比較多啦:

public ScaleAnimation(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ScaleAnimation(float fromX, float toX, float fromY, float toY) {
  
}

public ScaleAnimation(float fromX, float toX, float fromY, float toY,
        float pivotX, float pivotY) {
   }


public ScaleAnimation(float fromX, float toX, float fromY, float toY,
        int pivotXType, float pivotXValue, int pivotYType, float pivotYValue
}

可以看到入?yún)⒎浅6嗄苷妫覀儊?lái)看一下各參數(shù)的含義:

float fromX           動(dòng)畫起始時(shí) X坐標(biāo)上的伸縮尺寸   
float toX               動(dòng)畫結(jié)束時(shí) X坐標(biāo)上的伸縮尺寸   
float fromY           動(dòng)畫起始時(shí)Y坐標(biāo)上的伸縮尺寸   
float toY               動(dòng)畫結(jié)束時(shí)Y坐標(biāo)上的伸縮尺寸   
int pivotXType      動(dòng)畫在X軸相對(duì)于物件位置類型   
float pivotXValue  動(dòng)畫相對(duì)于物件的X坐標(biāo)的開始位置   
int pivotYType      動(dòng)畫在Y軸相對(duì)于物件位置類型   
float pivotYValue  動(dòng)畫相對(duì)于物件的Y坐標(biāo)的開始位置 

1.3.3 translateAnimation

xml實(shí)現(xiàn):

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="0"
    android:toYDelta="1000"></translate>
</set>

JAVA代碼實(shí)現(xiàn):

 TranslateAnimation translateAnimation = new     TranslateAnimation(Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f,Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f);
    scaleAnimation.setDuration(200);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

構(gòu)造方法:

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
}


public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue) {
}

構(gòu)造方法各參數(shù)含義:

參數(shù)fromXType:開始時(shí)x軸相對(duì)于組件的位置類型赁严。
參數(shù)fromXValue:開始時(shí)x軸的坐標(biāo)。根據(jù)fromXType代表不同的意義粉铐。
參數(shù)toXType:結(jié)束時(shí)x軸相對(duì)于組件的位置類型疼约。
參數(shù)toXValue:結(jié)束時(shí)x軸的坐標(biāo)。根據(jù)toXType代表不同的意義蝙泼。
參數(shù)fromYType:開始時(shí)y軸相對(duì)于組件的位置類型程剥。
參數(shù)fromYValue:開始時(shí)y軸的坐標(biāo)。根據(jù)fromYType代表不同的意義踱承。
參數(shù)toYType:結(jié)束時(shí)y軸相對(duì)于組件的位置類型倡缠。
參數(shù)toYValue:結(jié)束時(shí)y軸的坐標(biāo)。根據(jù)toYType代表不同的意義茎活。

關(guān)于入?yún)r(shí)的參數(shù)值:


參數(shù)值詳解.png

1.3.4:RorateAnimation

XML方式:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="restart"
android:startOffset="0">
<rotate
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360"
    ></rotate>
</set>

Java代碼實(shí)現(xiàn):

RotateAnimation rotateAnimation = new RotateAnimation(0,-720,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scaleAnimation.setDuration(1000);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

構(gòu)造方法:

  public RotateAnimation(float fromDegrees, float toDegrees) {
}

public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {

}

public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
        int pivotYType, float pivotYValue) {
}

參數(shù):

fromDegrees:旋轉(zhuǎn)的起始角度 
toDegrees:旋轉(zhuǎn)的終止角度 
pivotX:該對(duì)象被旋轉(zhuǎn)的點(diǎn)的X坐標(biāo)昙沦,指定為一個(gè)絕對(duì)數(shù)字,其中0是左邊緣载荔。 
pivotY:對(duì)象被旋轉(zhuǎn)的點(diǎn)的Y坐標(biāo)盾饮,指定為一個(gè)絕對(duì)數(shù)字,其中0是頂部邊緣懒熙。

1.3.5自定義補(bǔ)間動(dòng)畫
在實(shí)際開發(fā)中丘损,可能會(huì)遇到四種基本動(dòng)畫無(wú)法實(shí)現(xiàn)的需求,這時(shí)可以自定義補(bǔ)間動(dòng)畫工扎,只需要繼承Animation徘钥,重寫這個(gè)抽象基類中的applyTransformation方法:

public class MyAnimtion extends Animation{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    super.applyTransformation(interpolatedTime, t);
}
}

interpolator表示動(dòng)畫的時(shí)間進(jìn)行比,值在0-1變化肢娘,第二個(gè)參數(shù)表示補(bǔ)間動(dòng)畫在不同時(shí)刻對(duì)View的變形程度.

對(duì)于Tween Animation的介紹就到此告一段落呈础,有想看各動(dòng)畫插值器效果和了解具體自定義補(bǔ)間動(dòng)畫的朋友可參考博客:http://blog.csdn.net/u011043551/article/details/65443751

1.4 屬性動(dòng)畫(Property Animation)

                                                         這是本篇文章的重點(diǎn),開發(fā)必考題
吸引一些大家的眼光.png
重點(diǎn).png

屬性動(dòng)畫是在Android3.0時(shí)引入的舆驶,在補(bǔ)間動(dòng)畫中,我們只能改變View的繪制效果而钞,View的真實(shí)屬性是沒(méi)有改變的沙廉,而屬性動(dòng)畫則可以直接改變View對(duì)象的屬性值,同時(shí)屬性動(dòng)畫幾乎可以對(duì)任何對(duì)象執(zhí)行動(dòng)畫臼节,而不是局限在View對(duì)象上撬陵。

首先簡(jiǎn)單了解一下相關(guān)屬性:
Duration動(dòng)畫的持續(xù)時(shí)間,默認(rèn)300ms网缝。
Time interpolation:時(shí)間差值巨税,乍一看不知道是什么,但是我說(shuō)LinearInterpolator粉臊、AccelerateDecelerateInterpolator垢夹,大家一定知道是干嘛的了,定義動(dòng)畫的變化率维费。
Repeat count and behavior:重復(fù)次數(shù)果元、以及重復(fù)模式;可以定義重復(fù)多少次犀盟;重復(fù)時(shí)從頭開始而晒,還是反向。
Animator sets: 動(dòng)畫集合阅畴,你可以定義一組動(dòng)畫倡怎,一起執(zhí)行或者順序執(zhí)行。
Frame refresh delay:幀刷新延遲贱枣,對(duì)于你的動(dòng)畫监署,多久刷新一次幀;默認(rèn)為10ms纽哥,但最終依賴系統(tǒng)的當(dāng)前狀態(tài)钠乏;基本不用管。

相關(guān)的類
ObjectAnimator 動(dòng)畫的執(zhí)行類春塌,后面詳細(xì)介紹
ValueAnimator 動(dòng)畫的執(zhí)行類晓避,后面詳細(xì)介紹
AnimatorSet 用于控制一組動(dòng)畫的執(zhí)行:線性,一起只壳,每個(gè)動(dòng)畫的先后執(zhí)行等俏拱。
AnimatorInflater 用戶加載屬性動(dòng)畫的xml文件
TypeEvaluator 類型估值,主要用于設(shè)置動(dòng)畫操作屬性的值吼句。
TimeInterpolator 時(shí)間插值锅必,上面已經(jīng)介紹。
總的來(lái)說(shuō)惕艳,屬性動(dòng)畫就是搞隐,動(dòng)畫的執(zhí)行類來(lái)設(shè)置動(dòng)畫操作的對(duì)象的屬性分蓖、持續(xù)時(shí)間,開始和結(jié)束的屬性值尔许,時(shí)間差值等,然后系統(tǒng)會(huì)根據(jù)設(shè)置的參數(shù)動(dòng)態(tài)的變化對(duì)象的屬性终娃。

屬性動(dòng)畫的基類是Animator味廊,它是一個(gè)抽象類,我們不會(huì)直接使用這個(gè)類棠耕,通常都是繼承它并重寫其中的相關(guān)方法余佛,Android SDK為開發(fā)者默認(rèn)提供了幾個(gè)子類,大多數(shù)情況下使用這些子類就可以完成開發(fā)任務(wù)了窍荧。

1.4.1:Evaluator
在介紹Animator之前辉巡,我們先來(lái)了解一個(gè)名為Evaluator的概念,它是用來(lái)控制屬性動(dòng)畫如何計(jì)算屬性值的蕊退,接口定義是TypeEvaluator郊楣,其中定義了evaluate方法,供不同類型的子類實(shí)現(xiàn)瓤荔。

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

而系統(tǒng)也有提供了一些常見(jiàn)的實(shí)現(xiàn)類净蚤,比如IntEvaluator、FloateEvaluator输硝、ArghEvaluator等今瀑。我們來(lái)看一下ArgbEvaluator的具體實(shí)現(xiàn),可以看到實(shí)現(xiàn)邏輯很簡(jiǎn)單点把,就是根據(jù)輸入的初始值和結(jié)束值及一個(gè)進(jìn)度比橘荠,計(jì)算出每一個(gè)進(jìn)度對(duì)于的ARGB值。

public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();

/**
 * Returns an instance of <code>ArgbEvaluator</code> that may be used in
 * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
 * be used in multiple <code>Animator</code>s because it holds no state.
 * @return An instance of <code>ArgbEvalutor</code>.
 *
 * @hide
 */
public static ArgbEvaluator getInstance() {
    return sInstance;
}

/**
 * This function returns the calculated in-between value for a color
 * given integers that represent the start and end values in the four
 * bytes of the 32-bit int. Each channel is separately linearly interpolated
 * and the resulting calculated values are recombined into the return value.
 *
 * @param fraction The fraction from the starting to the ending values
 * @param startValue A 32-bit int value representing colors in the
 * separate bytes of the parameter
 * @param endValue A 32-bit int value representing colors in the
 * separate bytes of the parameter
 * @return A value that is calculated to be the linearly interpolated
 * result, derived by separating the start and end values into separate
 * color channels and interpolating each one separately, recombining the
 * resulting values in the same way.
 */
public Object evaluate(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    float startA = ((startInt >> 24) & 0xff) / 255.0f;
    float startR = ((startInt >> 16) & 0xff) / 255.0f;
    float startG = ((startInt >>  8) & 0xff) / 255.0f;
    float startB = ( startInt        & 0xff) / 255.0f;

    int endInt = (Integer) endValue;
    float endA = ((endInt >> 24) & 0xff) / 255.0f;
    float endR = ((endInt >> 16) & 0xff) / 255.0f;
    float endG = ((endInt >>  8) & 0xff) / 255.0f;
    float endB = ( endInt        & 0xff) / 255.0f;

    // convert from sRGB to linear
    startR = (float) Math.pow(startR, 2.2);
    startG = (float) Math.pow(startG, 2.2);
    startB = (float) Math.pow(startB, 2.2);

    endR = (float) Math.pow(endR, 2.2);
    endG = (float) Math.pow(endG, 2.2);
    endB = (float) Math.pow(endB, 2.2);

    // compute the interpolated color in linear space
    float a = startA + fraction * (endA - startA);
    float r = startR + fraction * (endR - startR);
    float g = startG + fraction * (endG - startG);
    float b = startB + fraction * (endB - startB);

    // convert back to sRGB in the [0..255] range
    a = a * 255.0f;
    r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
    g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
    b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

    return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}

1.4.2:AnimatorSet
AnimatorSet是Animator的子類郎逃,用來(lái)組合多個(gè)Animator哥童,并指定這些Animator是順序播放還是同時(shí)播放。

1.4.3:ValueAnimator:
ValueAnimator是屬性動(dòng)畫最重要的一個(gè)類褒翰,繼承自Animator如蚜,它定義了屬性動(dòng)畫大部分的核心功能,包括計(jì)算各個(gè)幀的屬性值影暴,處理更新事件错邦,按照屬性值的類型控制計(jì)算規(guī)則等。
一個(gè)完整的屬性動(dòng)畫由以下兩部分組成型宙。
計(jì)算動(dòng)畫各個(gè)幀的相關(guān)屬性值
將這些屬性設(shè)置給指定的對(duì)象

ValueAnimator為開發(fā)者實(shí)現(xiàn)了第一部分的功能撬呢,第二部分功能由開發(fā)者自行設(shè)置,ValueAnimator的構(gòu)造函數(shù)是空實(shí)現(xiàn)妆兑,一般都是使用如下的靜態(tài)工程方法來(lái)進(jìn)行實(shí)例化魂拦。

  public ValueAnimator() {
}

public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    return anim;
}
public static ValueAnimator ofFloat(float... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setFloatValues(values);
    return anim;
}
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setValues(values);
    return anim;
}
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setObjectValues(values);
    anim.setEvaluator(evaluator);
    return anim;
}

獲取到實(shí)例后毛仪,接著需要設(shè)置動(dòng)畫持續(xù)時(shí)間、插值方式芯勘、重復(fù)次數(shù)等屬性值箱靴,然后啟動(dòng)動(dòng)畫,最后還需要為ValueAnimator注冊(cè)AnimatorUpdateListenner監(jiān)聽(tīng)器荷愕,并在這個(gè)監(jiān)聽(tīng)器的onAnimationUpdate方法中將計(jì)算出來(lái)的值設(shè)置給對(duì)象衡怀。我們從React Native這個(gè)開源框架中可以看到如下用法:


image.png

1.4.4 ObjectAnimator
ObjectAnimator是ValueAnimator的子類,封裝實(shí)現(xiàn)了上面所說(shuō)的第二部分的功能安疗。因此抛杨,在實(shí)際開發(fā)中用到最多的就是ObjectAnimator,只有在ObjectAnimator實(shí)現(xiàn)不了的場(chǎng)景下荐类,才考慮使用ValueAnimator怖现。ObjectAnimator和ValueAnimator在構(gòu)造實(shí)例時(shí)最大的不同是指需要指定動(dòng)畫作用的具體對(duì)象和對(duì)象屬性名,而且一般不需要注冊(cè)AnimatorUpdateListener監(jiān)聽(tīng)器玉罐,簡(jiǎn)單的旋轉(zhuǎn)View實(shí)例如下:

  public void rotateyAnimRun(View view)  
{  
     ObjectAnimator//  
     .ofFloat(view, "rotationX", 0.0F, 360.0F)//  
     .setDuration(500)//  
     .start();  
}  

那么對(duì)于屬性動(dòng)畫簡(jiǎn)單介紹就到此告一段落屈嗤,我們使用的越多就越能了解到它的強(qiáng)大之處,上面主要是講述屬性動(dòng)畫一些概念和簡(jiǎn)單實(shí)用方式吊输,而接下來(lái)我們通過(guò)幾個(gè)demo來(lái)知道屬性動(dòng)畫還可以怎么用恢共,實(shí)現(xiàn)什么樣的效果。

實(shí)際開發(fā)當(dāng)中璧亚,我們使用一下ViewPropertyAnimator來(lái)實(shí)現(xiàn)各種動(dòng)畫效果讨韭,而且是在是簡(jiǎn)單到難以想象。

使用方式:view.animate()后直接跟translation和alpha等想實(shí)現(xiàn)的效果
ImageView imageView = findViewById(R.id.iv);
imageView.animate().translationX(500).translationY(700).alpha(1f).setDuration(1000 );

我們看看實(shí)際操作方法有哪些癣蟋?


出自Hencoder.png
話不多說(shuō)先寫一個(gè)出自Hencoder的Demo
進(jìn)度.gif
public class SportView extends View{


public float getProgress() {
    return progress;
}

public void setProgress(float progress) {
    this.progress = progress;
    invalidate();
}

float progress = 0;

RectF arcRectF = new RectF();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);


public SportView(Context context) {
    this(context,null);
}

public SportView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs,0);
}

public SportView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


{
    paint.setTextSize(Utils.dpToPixel(30));
    paint.setTextAlign(Paint.Align.CENTER);
}


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //圓心位置
    float centerX = getWidth()/2;
    float centerY = getHeight()/2;
    //注意因?yàn)閳A弧也有高度的原因透硝,半徑應(yīng)該取設(shè)置小于寬度的一半
    float radius = getWidth()/5*2;

    paint.setColor(Color.parseColor("#E91E63"));
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeCap(Paint.Cap.ROUND);
    paint.setStrokeWidth(Utils.dpToPixel(10));
    //設(shè)置區(qū)域,四個(gè)參數(shù)對(duì)應(yīng)四個(gè)點(diǎn)坐標(biāo)
    arcRectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
    canvas.drawArc(arcRectF, 135, progress*2.7f, false, paint);

    paint.setColor(Color.WHITE);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawText((int)progress+"%",centerX,centerY-(paint.ascent()+paint.descent())/5*2,paint);
}}

xml中直接引用我們的自定義控件:

  <com.example.xjf.androidanimation.SportView
    android:id="@+id/sportView"
    android:layout_width="160dp"
    android:layout_height="160dp" />

最后在Activity中直接創(chuàng)建一個(gè)ObjectAnimator對(duì)象并傳入我們的進(jìn)度參數(shù)綁定View:

 SportView sportView = findViewById(R.id.sportView);
    ObjectAnimator animator = ObjectAnimator.ofFloat(sportView,"progress",0,65);
    animator.start();

通用功能

  1. setDuration(int duration) 設(shè)置動(dòng)畫時(shí)長(zhǎng)

單位是毫秒疯搅。

// imageView1: 500 毫秒
imageView1.animate()  [圖片上傳中...(AccelerateDecelerateInterpolator.gif-277ef4-1514647894330-0)]

    .translationX(500)
    .setDuration(500);

// imageView2: 2 秒
ObjectAnimator animator = ObjectAnimator.ofFloat(  
    imageView2, "translationX", 500);
animator.setDuration(2000);  
animator.start();  
不同時(shí)長(zhǎng)效果.gif
  1. setInterpolator(Interpolator interpolator) 設(shè)置 Interpolator
 // imageView1: 線性 Interpolator濒生,勻速
  imageView1.animate()  
    .translationX(500)
    .setInterpolator(new LinearInterpolator());

// imageView: 帶施法前搖和回彈的 Interpolator
ObjectAnimator animator = ObjectAnimator.ofFloat(  
    imageView2, "translationX", 500);
animator.setInterpolator(new AnticipateOvershootInterpolator());  
animator.start();  
linearInterpolator.gif

接下來(lái)簡(jiǎn)單介紹一下每一個(gè)Interpolator:

AccelerateDecelerateInterpolator

先加速再減速。這是默認(rèn)的 Interpolator幔欧,也就是說(shuō)如果你不設(shè)置的話罪治,那么動(dòng)畫將會(huì)使用這個(gè) Interpolator。

這個(gè)是一種最符合現(xiàn)實(shí)中物體運(yùn)動(dòng)的 Interpolator礁蔗,它的動(dòng)畫效果看起來(lái)就像是物體從速度為 0 開始逐漸加速觉义,然后再逐漸減速直到 0 的運(yùn)動(dòng)。它的速度 / 時(shí)間曲線以及動(dòng)畫完成度 / 時(shí)間曲線都是一條正弦 / 余弦曲線浴井。具體的效果如下:


AccelerateDecelerateInterpolator.gif

LinearInterpolator

勻速晒骇。


勻速.gif

AccelerateInterpolator

持續(xù)加速。

在整個(gè)動(dòng)畫過(guò)程中,一直在加速洪囤,直到動(dòng)畫結(jié)束的一瞬間徒坡,直接停止。

別看見(jiàn)它加速驟停就覺(jué)得這是個(gè)神經(jīng)病模型哦瘤缩,它很有用的喇完。它主要用在離場(chǎng)效果中,比如某個(gè)物體從界面中飛離剥啤,就可以用這種效果锦溪。它給人的感覺(jué)就會(huì)是「這貨從零起步,加速飛走了」铐殃。到了最后動(dòng)畫驟停的時(shí)候,物體已經(jīng)飛出用戶視野跨新,看不到了富腊,所以他們是并不會(huì)察覺(jué)到這個(gè)驟停的。

DecelerateInterpolator

持續(xù)減速直到 0域帐。

動(dòng)畫開始的時(shí)候是最高速度赘被,然后在動(dòng)畫過(guò)程中逐漸減速,直到動(dòng)畫結(jié)束的時(shí)候恰好減速到 0肖揣。
DecelerateInterpolator.gif

它的效果和上面這個(gè) AccelerateInterpolator 相反民假,適用場(chǎng)景也和它相反:它主要用于入場(chǎng)效果,比如某個(gè)物體從界面的外部飛入界面后停在某處龙优。它給人的感覺(jué)會(huì)是「咦飛進(jìn)來(lái)個(gè)東西羊异,讓我仔細(xì)看看,哦原來(lái)是 XXX」彤断。

AnticipateInterpolator

先回拉一下再進(jìn)行正常動(dòng)畫軌跡野舶。效果看起來(lái)有點(diǎn)像投擲物體或跳躍等動(dòng)作前的蓄力。
AnticipateInterpolator.gif

如果是圖中這樣的平移動(dòng)畫宰衙,那么就是位置上的回拉平道;如果是放大動(dòng)畫,那么就是先縮小一下再放大供炼;其他類型的動(dòng)畫同理一屋。

這個(gè) Interpolator 就有點(diǎn)耍花樣了袋哼。沒(méi)有通用的適用場(chǎng)景冀墨,根據(jù)具體需求和設(shè)計(jì)師的偏好而定。

OvershootInterpolator

動(dòng)畫會(huì)超過(guò)目標(biāo)值一些涛贯,然后再?gòu)椈貋?lái)轧苫。效果看起來(lái)有點(diǎn)像你一屁股坐在沙發(fā)上后又被彈起來(lái)一點(diǎn)的感覺(jué)。


OvershootInterpolator.gif

BounceInterpolator

在目標(biāo)值處彈跳。有點(diǎn)像玻璃球掉在地板上的效果含懊。


BounceInterpolator.gif

CycleInterpolator

這個(gè)也是一個(gè)正弦 / 余弦曲線身冬,不過(guò)它和 AccelerateDecelerateInterpolator 的區(qū)別是,它可以自定義曲線的周期岔乔,所以動(dòng)畫可以不到終點(diǎn)就結(jié)束酥筝,也可以到達(dá)終點(diǎn)后回彈,回彈的次數(shù)由曲線的周期決定雏门,曲線的周期由 CycleInterpolator() 構(gòu)造方法的參數(shù)決定嘿歌。

參數(shù)為 0.5f:
0.5f.gif

參數(shù)為2f:
2f.gif

PathInterpolator

自定義動(dòng)畫完成度 / 時(shí)間完成度曲線。

用這個(gè) Interpolator 你可以定制出任何你想要的速度模型茁影。定制的方式是使用一個(gè) Path 對(duì)象來(lái)繪制出你要的動(dòng)畫完成度 / 時(shí)間完成度曲線宙帝。例如:

Path interpolatorPath = new Path();

...

// 勻速
interpolatorPath.lineTo(1, 1);  
image.png
006tKfTcly1fj8ffqb46bg30lg0buduv.gif
Path interpolatorPath = new Path();

...

// 先以「動(dòng)畫完成度 : 時(shí)間完成度 = 1 : 1」的速度勻速運(yùn)行 25%
interpolatorPath.lineTo(0.25f, 0.25f);  
// 然后瞬間跳躍到 150% 的動(dòng)畫完成度
interpolatorPath.moveTo(0.25f, 1.5f);  
// 再勻速倒車,返回到目標(biāo)點(diǎn)
interpolatorPath.lineTo(1, 1);  
image.png

006tKfTcly1fj8jsmxr3eg30lg0buto5.gif

本篇文章到此結(jié)束啦募闲,感謝Hencoder開源的自定義View學(xué)習(xí)資料以及Android高級(jí)進(jìn)階一書所講解的知識(shí)步脓。本文都是出自上面文章,自己算作學(xué)習(xí)資料吧浩螺,下次更新一個(gè)比較完整的動(dòng)畫效果來(lái)總結(jié)一下靴患,以及補(bǔ)上5.0之后的一些動(dòng)畫新特性,再見(jiàn)要出。

感謝:http://hencoder.com/
感謝:Android高級(jí)進(jìn)階

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸳君,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子患蹂,更是在濱河造成了極大的恐慌或颊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件传于,死亡現(xiàn)場(chǎng)離奇詭異饭宾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)格了,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門看铆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人盛末,你說(shuō)我怎么就攤上這事弹惦。” “怎么了悄但?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵棠隐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我檐嚣,道長(zhǎng)助泽,這世上最難降的妖魔是什么啰扛? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮嗡贺,結(jié)果婚禮上隐解,老公的妹妹穿的比我還像新娘。我一直安慰自己诫睬,他們只是感情好煞茫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著摄凡,像睡著了一般续徽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亲澡,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天钦扭,我揣著相機(jī)與錄音,去河邊找鬼床绪。 笑死客情,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的会涎。 我是一名探鬼主播裹匙,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼瑞凑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼末秃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起籽御,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤练慕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后技掏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铃将,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年哑梳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劲阎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸠真,死狀恐怖悯仙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吠卷,我是刑警寧澤锡垄,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站祭隔,受9級(jí)特大地震影響货岭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一千贯、第九天 我趴在偏房一處隱蔽的房頂上張望屯仗。 院中可真熱鬧,春花似錦丈牢、人聲如沸祭钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慌核。三九已至,卻和暖如春申尼,著一層夾襖步出監(jiān)牢的瞬間垮卓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工师幕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粟按,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓霹粥,卻偏偏與公主長(zhǎng)得像灭将,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子后控,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • Android框架提供了兩個(gè)動(dòng)畫系統(tǒng)庙曙,屬性動(dòng)畫(property animation )和視圖動(dòng)畫(view an...
    wenny826閱讀 2,355評(píng)論 0 2
  • 1 背景 不能只分析源碼呀,分析的同時(shí)也要整理歸納基礎(chǔ)知識(shí)浩淘,剛好有人微博私信讓全面說(shuō)說(shuō)Android的動(dòng)畫捌朴,所以今...
    未聞椛洺閱讀 2,705評(píng)論 0 10
  • 本文主要是針對(duì)android 中的動(dòng)畫進(jìn)行詳細(xì)描述,并簡(jiǎn)單分析原理张抄;一砂蔽、概述Android動(dòng)畫分為三種:幀動(dòng)畫(F...
    暮染1閱讀 777評(píng)論 0 0
  • 轉(zhuǎn)載一篇高質(zhì)量博文,原地址請(qǐng)戳這里轉(zhuǎn)載下來(lái)方便今后查看署惯。1 背景不能只分析源碼呀左驾,分析的同時(shí)也要整理歸納基礎(chǔ)知識(shí),...
    Elder閱讀 1,942評(píng)論 0 24
  • 1极谊、錯(cuò)誤:tableView诡右。 Assertion failurein-[UITableView-configur...
    MdWhat閱讀 674評(píng)論 4 2