屬性動畫


屬性動畫實際上是一種不斷地對值進行操作的機制寞蚌,并將值賦值到指定對象的指定屬性上,可以是任意對象的任意屬性允耿。屬性動畫機制已經(jīng)不只是針對于View來設計的了奕坟,也不限定于只能實現(xiàn)移動、縮放罗侯、旋轉和淡入淡出這幾種動畫操作器腋,同時也不再只是一種視覺上的動畫效果了。所以我們?nèi)匀豢梢詫⒁粋€View進行移動或者縮放钩杰,但同時也可以對自定義View中的Point對象進行動畫操作了纫塌。我們只需要告訴系統(tǒng)動畫的運行時長,需要執(zhí)行哪種類型的動畫讲弄,以及動畫的初始值和結束值措左,剩下的工作就可以全部交給系統(tǒng)去完成了。


  • ValueAnimator
    ValueAnimator是整個屬性動畫機制當中最核心的一個類避除,屬性動畫的運行機制是通過不斷地對值進行操作來實現(xiàn)的怎披,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。它的內(nèi)部使用一種時間循環(huán)的機制來計算值與值之間的動畫過渡瓶摆,我們只需要將初始值和結束值提供給ValueAnimator凉逛,并且告訴它動畫所需運行的時長,那么ValueAnimator就會自動幫我們完成從初始值平滑地過渡到結束值這樣的效果群井。除此之外状飞,ValueAnimator還負責管理動畫的播放次數(shù)、播放模式书斜、以及對動畫設置監(jiān)聽器等诬辈,確實是一個非常重要的類。
    示例:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.start()

setStartDelay()方法來設置動畫延遲播放的時間
setRepeatCount()設置動畫循環(huán)播放的次數(shù)
setRepeatMode()方法設置循環(huán)播放的模式菩佑,循環(huán)模式包括RESTART和REVERSE兩種自晰,分別表示重新播放和倒序播放的意思凝化。


  • ObjectAnimator
    ObjectAnimator是經(jīng)常接觸到的類稍坯,它是繼承自ValueAnimator的,底層的動畫實現(xiàn)機制也是基于ValueAnimator來完成的。提供了ofInt瞧哟、ofFloat混巧、ofObject。
    示例:
float curTranslationX = textview.getTranslationX(); 
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX); 
animator.setDuration(5000); 
animator.start();

調(diào)用了TextView的getTranslationX()方法來獲取到當前TextView的translationX的位置勤揩,然后ofFloat()方法的第二個參數(shù)傳入"translationX"咧党,緊接著后面三個參數(shù)用于告訴系統(tǒng)TextView應該怎么移動。ofFloat()方法的第二個參數(shù)可以是

  • alpha:表示View對象的透明度
  • rotation陨亡、rotationX傍衡、rotationY:控制View對象圍繞支點進行2D和3D旋轉
  • translationX、translationY:作為一種增量來控制著View對象從它布局容器的左上角坐標開始的位置负蠕。
  • x蛙埂、y:描述了View對象在它的容器中的最終位置,是最初的左上角坐標和translationX和translationY值得累加
  • scaleY遮糖、scaleX:控制View對象圍繞它的支點進行2D縮放
    ObjectAnimator內(nèi)部的工作機制并不是直接對傳入的屬性名進行操作的绣的,而是會去尋找這個屬性名對應的get和set方法,這兩個方法是由View對象提供的欲账,因此alpha屬性所對應的get和set方法應該就是:
public void setAlpha(float value);  
public float getAlpha();

  • 組合動畫
    實現(xiàn)組合動畫功能主要需要借助AnimatorSet這個類屡江,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)將會返回一個AnimatorSet.Builder的實例赛不,AnimatorSet.Builder中包括以下四個方法:
    • after(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之后執(zhí)行
    • after(long delay) 將現(xiàn)有動畫延遲指定毫秒后執(zhí)行
    • before(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之前執(zhí)行
    • with(Animator anim) 將現(xiàn)有動畫和傳入的動畫同時執(zhí)行

示例:
TextView先從屏幕外移動進屏幕惩嘉,然后開始旋轉360度,旋轉的同時進行淡入淡出操作俄删。

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

  • Animator監(jiān)聽器
    Animator類當中提供了一個addListener()方法宏怔,這個方法接收一個AnimatorListener,我們只需要去實現(xiàn)這個AnimatorListener就可以監(jiān)聽動畫的各種事件了畴椰。
    ValueAnimator臊诊、ObjectAnimator、AnimatorSet都是繼承自Animator的斜脂,都可以使用addListener()這個方法抓艳。
    添加一個監(jiān)聽器的代碼如下:
anim.addListener(new AnimatorListener(){
    @Override
    public void onAnimationStart(Animator animator){ //在動畫開始的時候調(diào)用
   
    }

     @Override
    public void onAnimationRepeat(Animator animator){ //動畫重復執(zhí)行的時候調(diào)用
    }

     @Override
    public void onAnimationEnd(Animator animator){ //在動畫結束的時候調(diào)用
        //刪除動畫
        Log.e(TAG, "onAnimationEnd");  
        ViewGroup parent = (ViewGroup) mBlueBall.getParent();  
        if (parent != null)  
            parent.removeView(mBlueBall);                 
    }

       @Override
    public void onAnimationCancel(Animator animator){ //在動畫被取消的時候調(diào)用
    }
});

有時并不用將這四個接口都實現(xiàn),Android提供了一個適配器類帚戳,叫作AnimatorListenerAdapter玷或,使用這個類就可以解決掉實現(xiàn)接口繁瑣的問題了,如下所示:

anim.addListener(new AnimatorListenerAdapter(){
});

向addListener()方法中傳入這個適配器對象片任,由于AnimatorListenerAdapter中已經(jīng)將每個接口都實現(xiàn)好了偏友,所以這里不用實現(xiàn)任何一個方法也不會報錯。因此对供,如果想監(jiān)聽動畫結束這個事件位他,就只需要單獨重寫這一個方法就可以了氛濒,如下所示:

anim.addListener(new AnimatorListenerAdapter(){
    @Override
    public void onAnimationEnd(Animator animator){ //在動畫結束的時候調(diào)用
    }
});

  • 使用XML編寫動畫
    通過XML來編寫動畫在重用方面將會變得非常輕松,比如某個通用的動畫編寫到XML里面鹅髓,可以在各個界面當中輕松去重用它舞竿。
    如果想要使用XML來編寫動畫,首先要在res目錄下面新建一個animator文件夾窿冯,所有屬性動畫的XML文件都應該存放在這個文件夾當中骗奖。然后在XML文件中我們一共可以使用如下三種標簽:
<animator>  對應代碼中的ValueAnimator
<objectAnimator>  對應代碼中的ObjectAnimator
<set>  對應代碼中的AnimatorSet

示例:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  **
    android:valueTo="0"  **
    android:valueType="floatType"  **
    android:propertyName="alpha"/>**

使用XML來完成復雜的組合動畫操作:

<set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="sequentially">
        <objectAnimator
            andorid:duration="2000"
            android:propertyName="translationX"
            android:valueFrom="-500"
            andorid:valueTo="0"
            android:valueType="floatType"
        />
        <set android:ordering="together"
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="rotation"
                android:valueFrom="0"
                andorid:valueTo="360"
                android:valueType="floatType"
            />
        <set android:ordering="sequentially">
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="alpha"
                android:valueFrom="1"
                andorid:valueTo="0"
                android:valueType="floatType"
            />
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="alpha"
                android:valueFrom="0"
                andorid:valueTo="1"
                android:valueType="floatType"
            />
        </set>
    </set>
</set>

使用set標簽,orderring屬性設置為together醒串,sequentially表示一個接一個執(zhí)行
在代碼中把文件加載進來并將動畫啟動:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file); 
animator.setTarget(view);  
animator.start();

調(diào)用AnimatorInflater的loadAnimator來將XML動畫文件加載進來执桌,然后再調(diào)用setTarget()方法將這個動畫設置到某一個對象上面,最后再調(diào)用start()方法啟動動畫芜赌。

  • View的animate()方法
    animate()方法是屬性動畫的一種簡寫方式
view.animate()
            .alpha(0)
            .y(300)
            .setDuration(300)
            .withStartAction(new Runnable() {
                @Override
                 public void run(){
                 } 
            })
            .withEndAction(new Runnable() {
                  @Override
                  public void run(){
                      runOnUIThread(new Runnable(){
                            @Override
                            public void run(){
                            }
                      });      
                  }
            }).start();

  • ValueAnimator的高級用法
    如果有一個自定義的View鼻吮,在這個View當中有一個Point對象用于管理坐標,然后在onDraw()方法當中就是根據(jù)這個Point對象的坐標值來進行繪制的较鼓。也就是說椎木,如果我們可以對Point對象進行動畫操作,那么整個自定義View的動畫效果就有了博烂。
    TypeEvaluator的作用就是告訴動畫系統(tǒng)如何從初始值過度到結束值香椎。ValueAnimator.ofFloat()方法就是實現(xiàn)了初始值與結束值之間的平滑過度,就是因為系統(tǒng)內(nèi)置了一個FloatEvaluator禽篱,它通過計算告知動畫系統(tǒng)如何從初始值過度到結束值畜伐,我們來看一下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);
    }
}

loatEvaluator實現(xiàn)了TypeEvaluator接口,然后重寫evaluate()方法躺率。evaluate()方法當中傳入了三個參數(shù)玛界,第一個參數(shù)fraction用于表示動畫的完成度的,根據(jù)它來計算當前動畫的值應該是多少悼吱,第二第三個參數(shù)分別表示動畫的初始值和結束值慎框。

  • 對象的動畫操作
    因為系統(tǒng)完全無法知道如何從初始對象過度到結束對象,需要實現(xiàn)一個自己的TypeEvaluator來告知系統(tǒng)如何進行過度后添。
public class Point {
    private float x;
    private float y;

    public Point(float x, float y){
        this.x = x;
        this.y = y;
    }
    public float getX(){
        return x;
    }
    public float getY(){
        reutrn y;
    }
}

Point類只有x和y兩個變量用于記錄坐標的位置笨枯,接下來定義PointEvaluator:

public valss PointEvaluator implements TypeEvaluator{
    @Overrride
    public Object evaluate(float fraction, Object startValue, Object endValue){
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint + fraction*(endPoint.getX() - startPoint.getX());
        float y = startPoint + fraction*(endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        reutrn point;
    }
}

PointEvaluator同樣實現(xiàn)了TypeEvaluator接口并重寫了evaluate()方法。在evaluate()方法中的先將startValue和endValue強制轉為Point對象遇西,然后根據(jù)fraction來計算當前動畫的x和y的值馅精,然后封裝到一個新的Point對象返回。
對Point對象進行動畫操作:
新建一個MyAninView繼承自View:

public class MyAnimView extends View{
    public static final float RADIUS = 50f;
    private Point currentPoint;
    private Paint mPaint;

    public MyAnimView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//用于繪制時消除鋸齒
        mPaint.setColor(Color.BULE);
    }

    @Override
    protected void OnDraw(Canvas canvas){
        if (currentPoint == null){
            currentPoint = new Point(RADIUS , RADIUS );
            drawCircle(canvas);
            startAnimation();
        } else{
            drawCircle(canvas);
        }
    }
    private void drawCircle(Canvas canvas){
        float x = currentPoint.getX();
        float y = currentPoint.getY();
        canvas.drawCircle(x, y, RADIUS, mPaint);
    }
    private void startAnimation() {
        Point startPoint = new Point(RADIUS, RADIUS);
        Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
        valueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
            @Override
            public void onAnimationUpdate(ValueAnimator animation){
                currentPoint = (Point) animation.getAnimateValue();
                invalidate(); //請求重新draw()粱檀,但只會繪制調(diào)用者本身
            }
        });
        anim.setDuration(5000);
        anim.start();
    }
}

首先在自定義View的構造方法當中初始化了一個Paint對象作為畫筆洲敢,并將畫筆顏色設置為藍色,接著在onDraw()方法當中進行繪制。這里我們繪制的邏輯是由currentPoint這個對象控制的彻磁,如果currentPoint對象不等于空,那么就調(diào)用drawCircle()方法在currentPoint的坐標位置畫出一個半徑為50的圓傅蹂,如果currentPoint對象是空哮塞,那么就調(diào)用startAnimation()方法來啟動動畫。


  • 布局動畫
    布局動畫是指作用在ViewGroup上凳谦,給ViewGroup增加View時添加一個動畫過度效果忆畅。
    最簡單的布局動畫是在IViewGroup的XML中,當ViewGroup添加View時尸执,子View會呈現(xiàn)逐漸顯示Android默認的過渡效果家凯,:
android:animateLayoutChanges="true"

可以通過LayoutAnimationController類來自定義一個子View的過渡效果:

Linearlayout ll = (LinearLayout) findViewById(R.id.ll);
//設置過渡動畫
ScaleAnimation sa  = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(2000);
//設置布局動畫的顯示屬性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//為ViewGroup設置布局動畫
ll.setLayoutAnimation(lac);

通過以上代碼,給LinearLayout增加了一個視圖動畫如失,讓子View在出現(xiàn)的時候绊诲,有一個縮放的動畫效果。
LayoutAnimationController構造函數(shù)的第一個參數(shù)是需要作用的動畫褪贵,第二個參數(shù)是每個子View顯示的delay時間掂之。當delay不為0時,可以設置子View的順序:

  • LayoutAnimationController.ORDER_NORMAL
  • LayoutAnimationController.ORDER_RANDOM 隨機
  • LayoutAnimationController.ORDER_REVERSE 反序

  • Interpolator插值器
    這個方法主要是用來控制android動畫的執(zhí)行速率脆丁,可以使存在的動畫效果:
  • AccelerateInterpolator ——在動畫開始的地方速率改變比較慢世舰,然后開始加速
  • AnticipateInterpolator ——開始的時候向后然后向前甩
  • AnticipateOvershootInterpolator ——開始的時候向后然后向前甩一定值后返回最后的值
  • BounceInterpolator ——開始時彈出,動畫結束的時候彈起
  • CycleInterpolator ——動畫循環(huán)播放特定的次數(shù)槽卫,速率改變沿著正弦曲線
  • DecelerateInterpolator—— 在動畫開始的地方快然后慢
  • LinearInterpolator ——以常量速率改變
  • OvershootInterpolator ——向前甩一定值后再回到原來位置

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跟压,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歼培,更是在濱河造成了極大的恐慌震蒋,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躲庄,死亡現(xiàn)場離奇詭異查剖,居然都是意外死亡,警方通過查閱死者的電腦和手機噪窘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門梗搅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人效览,你說我怎么就攤上這事无切。” “怎么了丐枉?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵哆键,是天一觀的道長。 經(jīng)常有香客問我瘦锹,道長籍嘹,這世上最難降的妖魔是什么闪盔? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮辱士,結果婚禮上泪掀,老公的妹妹穿的比我還像新娘。我一直安慰自己颂碘,他們只是感情好异赫,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著头岔,像睡著了一般塔拳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峡竣,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天靠抑,我揣著相機與錄音,去河邊找鬼适掰。 笑死颂碧,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的类浪。 我是一名探鬼主播稚伍,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼戚宦!你這毒婦竟也來了个曙?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤受楼,失蹤者是張志新(化名)和其女友劉穎垦搬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艳汽,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡猴贰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了河狐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片米绕。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖馋艺,靈堂內(nèi)的尸體忽然破棺而出栅干,到底是詐尸還是另有隱情,我是刑警寧澤捐祠,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布碱鳞,位于F島的核電站,受9級特大地震影響踱蛀,放射性物質(zhì)發(fā)生泄漏窿给。R本人自食惡果不足惜贵白,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崩泡。 院中可真熱鬧禁荒,春花似錦、人聲如沸角撞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靴寂。三九已至,卻和暖如春召耘,著一層夾襖步出監(jiān)牢的瞬間百炬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工污它, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剖踊,地道東北人。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓衫贬,卻偏偏與公主長得像德澈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子固惯,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

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