android動(dòng)畫入門:屬性動(dòng)畫

前言

屬性動(dòng)畫是API11加入的新特性考蕾,旨在實(shí)現(xiàn)更加絢麗的動(dòng)畫效果,不再像 View動(dòng)畫那樣只能支持四種簡單的變換昭殉,其可以對(duì)任意對(duì)象的屬性進(jìn)行動(dòng)畫而不僅僅局限于View苞七。

由于屬性動(dòng)畫從API11才加入,我們可以采用nineoldandroids開源動(dòng)畫庫來兼容以前版本挪丢,Nineoldandroids的功能和系統(tǒng)原生的android.animation.*中類的功能完全一致蹂风,使用方法也完全一樣,只要我們用nineoldandroids 來編寫動(dòng)畫乾蓬,就可以在所有的Android系統(tǒng)上運(yùn)行惠啄。

屬性動(dòng)畫的使用

下面來集中學(xué)習(xí)屬性動(dòng)畫中的幾個(gè)常用的動(dòng)畫類:

  • ValueAnimator
  • ObjectAnimator 其繼承自 ValueAnimator
  • AnimatorSet 動(dòng)畫集合
1. ValueAnimator

關(guān)于ValueAnimator,首先先要理解一個(gè)概念,ValueAnimator 的動(dòng)畫并不是直接作用于對(duì)象的撵渡,而是對(duì)一個(gè)值做動(dòng)畫融柬,然后我們通過監(jiān)聽這個(gè)值得變化,來完成我們對(duì)所要變換的對(duì)象的屬性值的修改趋距,從而實(shí)現(xiàn)動(dòng)畫效果粒氧。下面通過一個(gè)例子,可能會(huì)更好理解:

private void startAnimation(final View view, final int start ,final int end){
        ValueAnimator valueAnimator =ValueAnimator.ofInt(1,100);
        valueAnimator.setDuration(3000).start();
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
            
            private IntEvaluator evaluator = new IntEvaluator();
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {                
                //獲取當(dāng)前動(dòng)畫進(jìn)度的比值
                float fraction = animation.getAnimatedFraction();
                //估值器通過比值計(jì)算變換后的寬度
                view.getLayoutParams().width = evaluator.evaluate(fraction, start, end);
                view.requestLayout();
            }
        });
        
    }

從上面的例子可以看出节腐,ValueAnimator 將整數(shù)值從1 變換至 100外盯,間隔時(shí)間是 3000 毫秒,我們需要添加 AnimatorUpdateListener 來監(jiān)聽這個(gè)變換過程翼雀,在onAnimationUpdate 方法中饱苟,通過獲取動(dòng)畫的變換進(jìn)度來最終確定變換結(jié)果,并完成對(duì) 對(duì)象屬性值的修改狼渊。

2. ObjectAnimator

ObjectAnimator 繼承自 ValueAnimator 箱熬, 并做了新的封裝。ObjectAnimator 允許指定 作用的對(duì)象以及變換的屬性狈邑,我們只需要調(diào)用其提供的方法,剩下復(fù)雜的變換過程城须,交給ObjectAnimator來完成即可。
在使用ObjectAnimator之前官地,我們先來看一下幾個(gè)常用的View屬性成員:

  • translationX酿傍,translationY:X/Y 軸方向偏移量的變化,相對(duì)于view 自身驱入。
  • rotationX赤炒,rotationY:沿 X/Y軸方向旋轉(zhuǎn)。
  • scaleX亏较,scaleY:沿 X/Y 軸縮放莺褒。
  • x,y:改變view在整個(gè)屏幕上的絕對(duì)坐標(biāo)雪情。
  • alpha:透明度的變化遵岩。

下面舉例來說明一下:

//沿Y軸向右平移100px ,正數(shù)為向右巡通,負(fù)數(shù)為向左 
ObjectAnimator.ofFloat(view, "translationY", 100).start();

ObjectAnimator類提供了ofInt尘执、ofFloat、ofObject 等常用的方法宴凉,接下來我們來看一下這些方法調(diào)用時(shí)都需要什么參數(shù):

/**
     * 
     * @param target
     * @param propertyName
     * @param values
     * @return
     */
 public static ObjectAnimator ofFloat(Object target, String propertyName, float... values);

從上面這個(gè)例子來看誊锭,target,propertyName 很好理解弥锄,分別是 作用的對(duì)象及 對(duì)象的屬性名丧靡。 重點(diǎn)來說一下 蟆沫, values。values 是可以設(shè)置多個(gè)的温治,如下所示:

ObjectAnimator.ofFloat(view, "translationY", 100).start();
ObjectAnimator.ofFloat(view, "translationY", 100,150,200).start();

當(dāng)只設(shè)置一個(gè)時(shí)就把通過getTranslationY()反射獲取的值作為初始值饭庞,設(shè)置的值作為終點(diǎn);如果設(shè)置兩個(gè)或多個(gè)熬荆,那么就會(huì)不斷的改變屬性值舟山,而初始值則為 第一個(gè)屬性值。

ObjectAnimator是通過不斷地調(diào)用setXXX方法更新屬性值來達(dá)到動(dòng)畫的效果的卤恳,這就要求Object必須聲明有g(shù)etXXX和setXXX方法捏顺。

**在ObjectAnimator 的使用過程中,總會(huì)遇到 設(shè)置的屬性名 在Object 中并不存在纬黎,或者沒有提供getXXX和setXXX方法,再或者提供了方法并不能實(shí)現(xiàn)預(yù)期的效果等這類問題劫窒。那么下面提供一個(gè)解決方法: 我們可以自定義一個(gè)類來包裝 目標(biāo)對(duì)象本今,并提供getXXX和setXXX方法。 **
下面通過舉例來看一下:

//首先定義包裝類
    private static class ViewWrapper{
        private View target;

        private ViewWrapper(View target) {
            super();
            this.target = target;
        }

        public int getWidth() {
            return target.getLayoutParams().width;
        }

        public void setWidth(int width) {
            this.target.getLayoutParams().width = width;
            target.requestLayout();
        }
    }

//直接使用
ViewWrapper viewWrapper = new ViewWrapper(view);
ObjectAnimator.ofFloat(viewWrapper, "width", 100).start();

說到這里主巍,有人想問:如果我想實(shí)現(xiàn)同時(shí)修改多個(gè)屬性怎么辦呢冠息? 別急,這個(gè)時(shí)候我們就需要用到 PropertyValuesHolder 類孕索。具體如下:

public static void performAnimate(View view) {

        PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat("scaleX", 1f,0.3f);
        PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat("scaleY", 1f,0.3f);
        PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat("translationY", 0, -250, 50);
        PropertyValuesHolder pvhTX = PropertyValuesHolder.ofFloat("translationX", 0, 200);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
                view, pvhSX, pvhSY, pvhTY, pvhTX).setDuration(1200);
        objectAnimator.setInterpolator(new DecelerateInterpolator());
        objectAnimator.start();
    }

并且PropertyValuesHolder還提供了 ofFloat()逛艰、ofInt()以及ofKeyframe 等方法。ofFloat()搞旭、ofInt() 與 ObjectAnimator 中的方法是相同使用的散怖,這里不做過多解釋,這里重點(diǎn)說一下肄渗,ofKeyframe镇眷,先來看下面的例子:

 Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
 Keyframe kf1 = Keyframe.ofFloat(.5f, 2f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);

關(guān)于Keyframe

//參數(shù) fraction 表示 動(dòng)畫進(jìn)度 value 表示動(dòng)畫播放到當(dāng)前進(jìn)度的參數(shù)值
public static Keyframe ofFloat(float fraction, float value) 
3. AnimationSet

AnimationSet 是動(dòng)畫集合,可以定義一組動(dòng)畫翎嫡,使用起來也是極其簡單的欠动。

ObjectAnimator objectAnimatorA = ObjectAnimator.ofFloat(view, "translationY", 100);
        ObjectAnimator objectAnimatorB = ObjectAnimator.ofFloat(view, "scaleY", 0.3f,1.5f);
        
        AnimatorSet animSet = new AnimatorSet();  
        animSet.setDuration(5000);  
//      animSet.playTogether(objectAnimatorA, objectAnimatorB, ...); //多個(gè)動(dòng)畫同時(shí)執(zhí)行
        animSet.play(objectAnimatorA).after(objectAnimatorB); //多個(gè)動(dòng)畫先后執(zhí)行
        animSet.start();  
4. 屬性動(dòng)畫監(jiān)聽器

這里提一下,屬性動(dòng)畫提供了兩個(gè)接口惑申,用于監(jiān)聽動(dòng)畫的播放過程具伍。

  • AnimatorUpdateListener
    監(jiān)聽動(dòng)畫整個(gè)播放過程
objectAnimatorA.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                // TODO Auto-generated method stub
                
            }
        });
  • AnimatorListener
    用于監(jiān)聽動(dòng)畫的開始,結(jié)束圈驼,取消以及重復(fù)播放人芽。
objectAnimatorA.addListener(new AnimatorListener() {
            
            @Override
            public void onAnimationStart(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationCancel(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
        });

插值器和估值器

關(guān)于插值器和估值器 這里主要講一下基本概念和使用方式。

1. 插值器(Interpolator)

插值器用于實(shí)現(xiàn)動(dòng)畫的非勻速變化碗脊,比較常見的插值器(Interpolator)有 LinearInterpolator (線性插值器:勻速動(dòng)畫)啼肩,AcclerateDecelerateInterpolator(加減速插值器:兩頭慢中間快)和 DecelerateInterpolator(減速插值器:動(dòng)畫速度越來越慢)橄妆。

objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
2. 估值器(TypeEvluator)

估值器用于 計(jì)算變換后的屬性值。通過動(dòng)畫進(jìn)行過程的百分比祈坠,確定當(dāng)前的屬性值害碾。

public class IntEvaluator implements TypeEvaluator<Integer> {
   
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

從源碼可以看出,其實(shí)就是一個(gè)簡單的估值算法赦拘。使用方式在 ValueAnimator 的示例中也有提到:

private void startAnimation(final View view, final int start ,final int end){
        ValueAnimator valueAnimator =ValueAnimator.ofInt(1,100);
        valueAnimator.setDuration(3000).start();
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

            private IntEvaluator evaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {                
                //獲取當(dāng)前動(dòng)畫進(jìn)度的比值
                float fraction = animation.getAnimatedFraction();
                //估值器通過比值計(jì)算變換后的寬度
                view.getLayoutParams().width = evaluator.evaluate(fraction, start, end);
                view.requestLayout();
            }
        });

    }

而 在ObjectAnimator中的使用可以 這樣使用:

objectAnimator.setEvaluator(new IntEvaluator());

不過從源碼來看慌随,估值器設(shè)置與否,實(shí)現(xiàn)的計(jì)算方式實(shí)際上與IntEvaluator是一樣的:

public int getIntValue(float fraction) {  
        if (mNumKeyframes == 2) {  
            if (firstTime) {  
                firstTime = false;  
                firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();  
                lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();  
                deltaValue = lastValue - firstValue;  
            }  
            if (mInterpolator != null) {  
                fraction = mInterpolator.getInterpolation(fraction);  
            }  
            if (mEvaluator == null) {  
                return firstValue + (int)(fraction * deltaValue);  
            } else {  
                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();  
            }  
        }  
        //...省略了很多代碼  
    }  

屬性動(dòng)畫的特殊使用

通過屬性動(dòng)畫 為 ViewGroup 的子元素設(shè)置動(dòng)畫躺同,相對(duì)于View 動(dòng)畫 阁猜,有了更多的選擇。
如果想開啟默認(rèn)的效果只需要在布局文件中添加:

android:animateLayoutChanges=”true”

如果想實(shí)現(xiàn)更炫的效果蹋艺,就需要用到LayoutTransition剃袍,其有5種動(dòng)畫狀態(tài)可以編輯:

  • LayoutTransition.APPEARING
    表示 View出現(xiàn)時(shí) 自身需要的動(dòng)畫效果
  • LayoutTransition.DISAPPEARING
    表示 View消失時(shí) 自身需要的動(dòng)畫效果
  • LayoutTransition.CHANGE_APPEARING
    表示 View出現(xiàn)時(shí) 整個(gè)容器所有View需要的動(dòng)畫效果
  • LayoutTransition.CHANGE_DISAPPEARING
    表示 View消失時(shí) 整個(gè)容器所有View消失的動(dòng)畫效果
  • LayoutTransition.CHANGE
    表示 除了以上這些情況,整個(gè)容器View發(fā)生改變時(shí)所需要的動(dòng)畫效果

ok捎谨,基礎(chǔ)就說這個(gè)多民效,下面來看一下具體使用:
item動(dòng)畫效果的設(shè)置:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "alpha", 0F, 1F).
                setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);

容器動(dòng)畫效果的設(shè)置:

        //pvhLeft, pvhTop, pvhRight, pvhBottom 必須添加,否則不顯示動(dòng)畫效果
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);

        PropertyValuesHolder animator = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, animator).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
        changeIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);

關(guān)于容器動(dòng)畫的設(shè)置涛救,pvhLeft, pvhTop, pvhRight, pvhBottom 必須添加畏邢,不然添加不了動(dòng)畫效果,至于具體原因检吆,大家可以自行學(xué)習(xí)舒萎,實(shí)際上我也不知道 >_<。
最后給留一個(gè)demo 蹭沛,LayoutTransition動(dòng)畫的實(shí)現(xiàn):
LayoutTransition動(dòng)畫的具體實(shí)現(xiàn)效果

總結(jié)

寫到這里臂寝,屬性動(dòng)畫的基本使用就差不多了,我在這里并沒有涉及屬性動(dòng)畫通過 xml定義的方式摊灭,總感覺屬性動(dòng)畫通過代碼實(shí)現(xiàn)會(huì)更簡單清晰一些交煞,如果大家有興趣可以去學(xué)習(xí)一下。

動(dòng)畫其他:View動(dòng)畫和幀動(dòng)畫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斟或,一起剝皮案震驚了整個(gè)濱河市素征,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萝挤,老刑警劉巖御毅,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異怜珍,居然都是意外死亡端蛆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門酥泛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來今豆,“玉大人嫌拣,你說我怎么就攤上這事〈舳悖” “怎么了异逐?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長插掂。 經(jīng)常有香客問我灰瞻,道長,這世上最難降的妖魔是什么辅甥? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任酝润,我火速辦了婚禮,結(jié)果婚禮上璃弄,老公的妹妹穿的比我還像新娘要销。我一直安慰自己,他們只是感情好夏块,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布蕉陋。 她就那樣靜靜地躺著,像睡著了一般拨扶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茁肠,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天患民,我揣著相機(jī)與錄音,去河邊找鬼垦梆。 笑死匹颤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的托猩。 我是一名探鬼主播印蓖,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼京腥!你這毒婦竟也來了赦肃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤公浪,失蹤者是張志新(化名)和其女友劉穎他宛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欠气,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厅各,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了预柒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片队塘。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袁梗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出憔古,到底是詐尸還是另有隱情遮怜,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布投放,位于F島的核電站奈泪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灸芳。R本人自食惡果不足惜涝桅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烙样。 院中可真熱鬧冯遂,春花似錦、人聲如沸谒获。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽批狱。三九已至裸准,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赔硫,已是汗流浹背炒俱。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爪膊,地道東北人权悟。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像推盛,于是被迫代替她去往敵國和親峦阁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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