Android動(dòng)畫機(jī)制與使用技巧

`# 前言

  • Android群英傳讀書筆記
    // SVG部分待補(bǔ)充

目錄

Android動(dòng)畫機(jī)制與使用技巧.png

Android View動(dòng)畫框架

  • Animation框架定義了幾種常見動(dòng)畫
    • 透明度 AlphaAnimation
    • 旋轉(zhuǎn) RotateAnimation
    • 縮放 ScaleAnimation
    • 位移 TranslateAnimation
    • 動(dòng)畫合集 AnimatorSet
  • 實(shí)現(xiàn)原理
    • 每次繪制視圖View所在ViewGroup中drawChild函數(shù)獲取該View的Animation的Transformation值
    • 調(diào)用canvas.concat(transformToApply.getMatrix())
    • 通過(guò)矩陣運(yùn)算完成動(dòng)畫幀
    • 如果動(dòng)畫沒(méi)有完成忌怎,就繼續(xù)調(diào)用invalidate()函數(shù)潦俺,啟動(dòng)下次繪制來(lái)驅(qū)動(dòng)動(dòng)畫
    • 從而完成整個(gè)動(dòng)畫的繪制
  • 缺點(diǎn):不具備交互性诊霹,視圖動(dòng)畫結(jié)束巷挥,但響應(yīng)事件位置不變
  • 優(yōu)點(diǎn):效率高,使用方便

Android屬性動(dòng)畫

  • 可以響應(yīng)事件
  • 通常是AnimatorSet和ObjectAnimator配合
  • ObjectAnimator能夠自動(dòng)驅(qū)動(dòng)

ObjectAnimator

  • 創(chuàng)建一個(gè)ObjectAnimator只需要通過(guò)它的靜態(tài)工廠類直接返回一個(gè)ObjectAnimator對(duì)象
  • 參數(shù)包括一個(gè)對(duì)象和對(duì)戲那個(gè)的屬性名,但這個(gè)屬性必須有Set和Get方法,內(nèi)部會(huì)通過(guò)反射機(jī)制調(diào)用Set方法修改對(duì)象屬性值
// 簡(jiǎn)單的位移動(dòng)畫
ObjectAnimator animator = ObjectAnimator.ofFloat(
  view,
  "translationX",
  300
);
animator.setDuration(300);
animator.start();
屬性 作用
translationX、translationY 作為增量控制View對(duì)象從它布局容器的左上角坐標(biāo)偏移的位置
rotation菱农、rotationX、rotationY 控制View對(duì)象圍繞支點(diǎn)進(jìn)行2D和3D旋轉(zhuǎn)
scaleX晤愧、scaleY 控制View對(duì)象圍繞支點(diǎn)進(jìn)行2D縮放
pivotX大莫、pivotY 控制View對(duì)象的支點(diǎn)位置,圍繞這個(gè)支點(diǎn)進(jìn)行旋轉(zhuǎn)和縮放變換處理官份,默認(rèn)情況下只厘,該支點(diǎn)的位置就是View對(duì)象的中心點(diǎn)
x烙丛、y 描述View對(duì)象在他容器的最終位置,是最初左上角坐標(biāo)和translationX羔味、translationY值得累計(jì)和
alpha View對(duì)象的透明度河咽,默認(rèn)為1,0為完全透明
  • 屬性動(dòng)畫的機(jī)制是通過(guò)反射調(diào)用Set和Get方法赋元,如果一個(gè)屬性沒(méi)有Get和Set方法應(yīng)該怎么辦忘蟹?google在應(yīng)用層提供了兩種解決方法
    • 通過(guò)自定義一個(gè)屬性類或者包裝類來(lái)間接的給這個(gè)屬性增加get、set方法
    • 通過(guò)ValueAnimator來(lái)實(shí)現(xiàn)
private static class WrapperView {
  private View mTraget;

  public WrapperView(View target) {
    mTarget = target;
  }

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

  public void setWidth(int width) {
    mTarget.getLayoutParams().width = width;
    mTarget.requestLayout();
  }
}
  • 通過(guò)上面的方法給屬性動(dòng)畫包裝一層搁凸,并提供了Get媚值、Set方法
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();

PropertyValuesHolder

  • 針對(duì)同一個(gè)對(duì)象的多個(gè)屬性,可以使用PropertyValuesHolder
PropertyValuesHolder pv1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pv2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pv3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pv1,pv2,pv3).setDuration(2000).start();

ValueAnimator

  • ObjectAnimator繼承自ValueAnimator
  • 可以看作一個(gè)數(shù)值發(fā)生器护糖,用來(lái)產(chǎn)生有規(guī)律的數(shù)字
  • AnimatorUpdateListener監(jiān)聽數(shù)值變換
ValueAnimator animator = ValueAnimator.ofFloat(0,100);
animator.setTarget(view);
animator.setDuration(1000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
    @Override 
    public void onAnimationUpdate(ValueAnimator animation) { 
    Float value = (Float) animation.getAnimatedValue(); 
    //do the animation! 
  }
});

動(dòng)畫事件監(jiān)聽

  • 一個(gè)完整的動(dòng)畫具有Start褥芒、Repeat、End嫡良、Cancel四個(gè)過(guò)程
  • 當(dāng)然一般用到最多的是End監(jiān)聽
 ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                super.onAnimationRepeat(animation);
            }

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
            }

        });

AnimatorSet

  • 同樣是對(duì)一個(gè)控件添加多個(gè)動(dòng)畫锰扶,AnimatorSet比PropertyValuesHolder多了精確控制動(dòng)畫順序的功能
  • 還是上面的例子
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(animator1,animator2,animator3);
set.start();
API 作用
playTogether() 一起播放
playSequentially() 順序播放
animSet.play(anim1).with(anim2) anim1和anim2一起播放
animSet.play(anim1).before(anim2) anim1在anim2之前播放
animSet.play(anim1).after(anim2) anim1在anim2之后播放

XML中使用屬性動(dòng)畫

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType"/>
  • 使用
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex);
anim.setTarget(view);
anim.start();

View的animate方法

  • Android3.0之后,View自帶animate方法直接驅(qū)動(dòng)屬性動(dòng)畫(相當(dāng)于簡(jiǎn)寫方法)
view.animate()
                .alpha(0)
                .y(300)
                .setDuration(3000)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {

                    }
                })
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {

                    }
                }).start();

Android布局動(dòng)畫

  • 作用在ViewGroup上寝受,給ViewGroup增加View時(shí)添加一個(gè)動(dòng)畫過(guò)渡效果
  • 最簡(jiǎn)單的例子
android:animateLayoutChanges="true"
  • 或者使用LayoutAnimationController類自定義一個(gè)View過(guò)渡效果
    • LayoutAnimationController.ORDER_NORMAL-- 順序
    • LayoutAnimationController.ORDER_RANDOM-- 隨機(jī)
    • LayoutAnimationController.ORDER_REVERSE-- 逆序
LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
// 設(shè)置過(guò)渡動(dòng)畫
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);
// 設(shè)置布局動(dòng)畫的顯示屬性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 為ViewGroup設(shè)置布局動(dòng)畫
ll.setLayoutAnimation(lac);


自定義動(dòng)畫

  • 實(shí)現(xiàn)applyTransformation即可
  • 根據(jù)情況覆蓋父類initialize方法實(shí)現(xiàn)一些初始化工作
/**
  * 電視機(jī)關(guān)閉效果
  */
public class CustomTV extends Animation {

    // 中心點(diǎn)坐標(biāo)
    private int mCenterWidth;
    private int mCenterHeight;

    /**
      * 實(shí)現(xiàn)初始化操作
      */
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(1000);
        // 動(dòng)畫結(jié)束后保留狀態(tài)
        setFillAfter(true);
        // 設(shè)置默認(rèn)插值器
        setInterpolator(new AccelerateInterpolator());
        mCenterWidth = width / 2;
        mCenterHeight = height / 2;
    }

    /**
      * 參數(shù)1:interpolatedTime:插值器的時(shí)間因子坷牛,由動(dòng)畫當(dāng)前完成的百分比和當(dāng)前時(shí)間所對(duì)應(yīng)的插值計(jì)算得來(lái),取值范圍0到1.0
      * 參數(shù)2:Transformation:矩陣的封裝類很澄,一般使用這個(gè)類獲得當(dāng)前的矩陣對(duì)象
      */
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        // 獲得當(dāng)前矩陣對(duì)象
        final Matrix matrix = t.getMatrix();
        // 通過(guò)改變matrix對(duì)象京闰,實(shí)現(xiàn)動(dòng)畫效果
        matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痴怨,隨后出現(xiàn)的幾起案子忙干,更是在濱河造成了極大的恐慌,老刑警劉巖浪藻,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乾翔,居然都是意外死亡爱葵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門反浓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萌丈,“玉大人,你說(shuō)我怎么就攤上這事雷则×疚恚” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵月劈,是天一觀的道長(zhǎng)度迂。 經(jīng)常有香客問(wèn)我藤乙,道長(zhǎng),這世上最難降的妖魔是什么惭墓? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任坛梁,我火速辦了婚禮,結(jié)果婚禮上腊凶,老公的妹妹穿的比我還像新娘划咐。我一直安慰自己,他們只是感情好钧萍,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布褐缠。 她就那樣靜靜地躺著,像睡著了一般风瘦。 火紅的嫁衣襯著肌膚如雪队魏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天弛秋,我揣著相機(jī)與錄音器躏,去河邊找鬼。 笑死蟹略,一個(gè)胖子當(dāng)著我的面吹牛登失,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挖炬,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼揽浙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了意敛?” 一聲冷哼從身側(cè)響起馅巷,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎草姻,沒(méi)想到半個(gè)月后钓猬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撩独,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年敞曹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片综膀。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澳迫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剧劝,到底是詐尸還是另有隱情橄登,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站拢锹,受9級(jí)特大地震影響谣妻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜面褐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一拌禾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧展哭,春花似錦湃窍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至役衡,卻和暖如春茵休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背手蝎。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工榕莺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棵介。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓钉鸯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親邮辽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唠雕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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