Android動(dòng)畫總結(jié)(屬性動(dòng)畫昨寞,補(bǔ)間動(dòng)畫关划,幀動(dòng)畫)

Android 動(dòng)畫總結(jié)

動(dòng)畫分類

Android中動(dòng)畫大概分為3類:

  1. TweenAnimation(補(bǔ)間動(dòng)畫)
  • TranslateAnimation
  • ScaleAnimation
  • RotateAnimation
  • AlphaAnimation
  1. FrameAnimation(幀動(dòng)畫)
  2. PropertyAnimation(屬性動(dòng)畫)

PropertyAnimation屬性動(dòng)畫

先來看屬性動(dòng)畫坡贺,這是潮流官辈,也是google推薦使用的。

屬性動(dòng)畫和一般的View動(dòng)畫的重要區(qū)別在于:

  1. 屬性動(dòng)畫控制的是實(shí)實(shí)在在的屬性遍坟,但是View動(dòng)畫只是產(chǎn)生一個(gè)動(dòng)畫拳亿,并沒有改變控件的屬性。
  2. View動(dòng)畫只能控制View的屬性愿伴,屬性動(dòng)畫可以控制所有屬性肺魁,只要這個(gè)屬性有g(shù)et/set 方法,是什么意思呢隔节?

例如 我們查看ImageView的源碼鹅经,可以看到它有一個(gè)私有屬性叫 alpha,表示透明度怎诫,同時(shí)有 setAlpha()/getAlpha() 方法瘾晃,所有我們就可以通過屬性動(dòng)畫來修改ImageView的Alpha的值來完成動(dòng)畫。

那么問題來了刽虹,到底動(dòng)畫是怎么產(chǎn)生的呢酗捌?簡(jiǎn)單的說就是,在動(dòng)畫執(zhí)行前你需要為動(dòng)畫設(shè)置初始值涌哲,結(jié)束值胖缤,動(dòng)畫時(shí)間(這是3個(gè)最基本的值),那么ValueAnimator類就可以通過這3個(gè)值計(jì)算出一串連續(xù)的數(shù)字阀圾,表示動(dòng)畫的過程哪廓。
例如:alpha值 from 0 to 255,如果時(shí)間設(shè)置成 255秒初烘,那么 ValueAnimator產(chǎn)生的值將為每秒變化1涡真,0,1,2,3,4,5,6...分俯,然后將這些值通過 setAlpha() 設(shè)置給ImageView,就完成了動(dòng)畫哆料。 當(dāng)然這個(gè)例子比較奇葩缸剪,但是我覺得比較好理解。

那么怎么來實(shí)踐我說的呢东亦?我們來看一個(gè)例子:

ValueAnimator

這是ValueAnimator的初級(jí)用法杏节,通過 ofFloat() 方法設(shè)置起始x坐標(biāo)起始x+100典阵,起始x坐標(biāo)奋渔,就是一個(gè)在x軸上一個(gè)來回100px的動(dòng)畫。區(qū)間是1000ms壮啊,注意為了將動(dòng)畫與控件相關(guān)聯(lián)(動(dòng)畫都是需要應(yīng)用到控件上)嫉鲸,需要添加一個(gè) AnimatorUpdateListener,這個(gè)回調(diào)就是用來產(chǎn)生一系列的中間值歹啼,然后我們?cè)诨卣{(diào)中將中間值設(shè)置給我們的ImageView玄渗,那么動(dòng)畫就完成了。

最后我們還是用Log將中間值都打印了出來染突,就更容易理解ValueAnimator就是用來產(chǎn)生一個(gè)值變化的序列的作用捻爷。當(dāng)然這里使用的是默認(rèn)的線性插值器,變化率是均勻的份企,如果使用其他插值器也榄,還可以產(chǎn)生不均勻的效果。

float fromX = ivLogo.getTranslationX();
ValueAnimator va = ValueAnimator.ofFloat(fromX, fromX + 100f, fromX);
va.setDuration(1000);

va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float v = (float) animation.getAnimatedValue();
        ivLogo.setTranslationX(v);
        Log.d(TAG, "v:" + v);
    }
});
va.start();

效果圖:

test1.gif

打印出來的Log:

D/ANIMATION: v:0.0
D/ANIMATION: v:0.14258027
D/ANIMATION: v:0.53691864
D/ANIMATION: v:1.2311637
D/ANIMATION: v:2.2070706
D/ANIMATION: v:3.3803642
D/ANIMATION: v:4.894352
...

D/ANIMATION: v:84.35657
D/ANIMATION: v:89.65131
D/ANIMATION: v:94.66184
D/ANIMATION: v:100.0
D/ANIMATION: v:94.66184
D/ANIMATION: v:89.651306
D/ANIMATION: v:84.35657
...
D/ANIMATION: v:6.679535
D/ANIMATION: v:4.894348
D/ANIMATION: v:3.380371
D/ANIMATION: v:2.2070618
D/ANIMATION: v:1.2311707
D/ANIMATION: v:0.53691864
D/ANIMATION: v:0.14257813
D/ANIMATION: v:0.0

理解了這個(gè)司志,我們就可以來看看下一個(gè)更加使用的類

ObjectAnimator

public final class ObjectAnimator extends ValueAnimator{...}

查看源碼就知道甜紫,ObjectAnimator是繼承于ValueAnimator的,ObjectAnimator使用起來更加簡(jiǎn)單骂远,因?yàn)閂alueAnimator還需要我們自己去回調(diào)囚霸,ObjectAnimator不需要寫回調(diào),只需要把要設(shè)置的屬性激才,控件拓型,區(qū)間等內(nèi)容告訴它,它自己幫我們完成動(dòng)畫瘸恼。

例子:

float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100f, fromX);
oa.setDuration(300);
oa.start();

oa.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationStart(Animator animation) {
        super.onAnimationStart(animation);
    }
});

效果和使用 ValueAnimator 是一樣的劣挫,所以就不貼圖了。剛剛我們說不需要回調(diào)东帅,但是這里又寫了一個(gè)回調(diào)的Listener是什么意思呢压固?大家大可把這個(gè)刪掉,我這里加上主要是為了說明如果想監(jiān)聽 動(dòng)畫結(jié)束靠闭,動(dòng)畫開始... 等中間過程帐我,ObjectAnimator也可以做到坎炼。只需要添加這個(gè) AnimatorListenerAdapter 抽象類。至于這里為什么是抽象類拦键,大家打開源碼就可以明白谣光,為了不實(shí)現(xiàn)回調(diào)的所有方法,特別加了一個(gè)抽象類實(shí)現(xiàn) AnimatorListener 接口矿咕,這樣就可以想覆蓋什么方法就覆蓋什么方法抢肛,不需要全部都覆蓋。這也可以理解為 設(shè)計(jì)模式中的適配器模式吧碳柱,原接口不符合我的要求,我需要一個(gè)適配器來完成轉(zhuǎn)換

AnimatorSet

如果上面兩個(gè)都理解了熬芜,那么這個(gè)就好理解了莲镣。AnimatorSet顧名思義就是動(dòng)畫的集合,我們要將幾個(gè)屬性動(dòng)畫放在一起運(yùn)行涎拉,或者按照序列執(zhí)行瑞侮,或者按任意順序執(zhí)行都可以實(shí)現(xiàn),看看例子:

ObjectAnimator oaScaleX = ObjectAnimator.ofFloat(ivLogo, "scaleX", 0, 1);
ObjectAnimator oaScaleY = ObjectAnimator.ofFloat(ivLogo, "scaleY", 0, 1);
ObjectAnimator oaRotation = ObjectAnimator.ofFloat(ivLogo, "rotation", 0, 360);
ObjectAnimator oaAlpha = ObjectAnimator.ofFloat(ivLogo, "alpha", 1, 0);


AnimatorSet as = new AnimatorSet();
as.play(oaScaleX).with(oaScaleY).with(oaRotation);
as.play(oaAlpha).after(oaRotation);
as.setDuration(1000);
as.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        ivLogo.setAlpha(1.0f);
        Log.d(TAG, "finish:" + ivLogo.getAlpha());
    }
});
as.start();

首先構(gòu)造了4個(gè) ObjectAnimator對(duì)象鼓拧,分別是沿X軸變形半火,沿Y軸變形,旋轉(zhuǎn)季俩,透明度钮糖,然后我們讓前3個(gè)一起執(zhí)行,之后在執(zhí)行漸變酌住,最后添加一個(gè)動(dòng)畫結(jié)束回調(diào)店归,將Alpha設(shè)置為1,表示不透明酪我,否則我們的控件就看不見了消痛。

效果圖:

test2.gif

使用xml文件配置屬性動(dòng)畫

android的套路,一定會(huì)允許你使用xml的方式來配置動(dòng)畫都哭,所以我們看看怎么配置:

ba358c4d-541e-4396-bd29-135a3be2142b.png

res 文件夾下新建文件夾 animator, 然后創(chuàng)建一個(gè) 動(dòng)畫xml文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">

    <objectAnimator
        android:duration="2000"
        android:propertyName="x"
        android:valueFrom="0"
        android:valueTo="200"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="2000"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="2000"
        android:propertyName="x"
        android:valueFrom="200"
        android:valueTo="400"
        android:valueType="floatType" />

</set>

android:ordering="sequentially"表示依次執(zhí)行秩伞,

android:propertyName="rotation"表示修改的屬性值

android:valueType="floatType"表示屬性值類型

其他的都好理解了。

然后在代碼中使用:

Animator animator = AnimatorInflater.loadAnimator(PropertyActivity.this, R.animator.animator_1);
animator.setTarget(ivLogo);
animator.start();

效果圖:

test3.gif

使用插值器

我們知道 ValueAnimator就是用來產(chǎn)生一些動(dòng)畫屬性中間值欺矫,但是默認(rèn)是均勻變化的纱新,插值器就是要使得產(chǎn)生的中間序列非均勻化,當(dāng)然不僅是非均勻這么簡(jiǎn)單汇陆,還有很多特效:
下面是官網(wǎng)提供的一些插值器怒炸,大家都可以試試,我這里只是拋磚引玉毡代,展示最簡(jiǎn)單的用法:

d6691284-7830-44e4-8abd-0ad2fc047b6a.png

使用方法:

float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100, fromX);
oa.setInterpolator(new BounceInterpolator());
oa.setDuration(1000);
oa.start();

只比前面多了一行:oa.setInterpolator(new BounceInterpolator());

效果圖:

test4.gif

TweenAnimation補(bǔ)間動(dòng)畫

如果上面的屬性動(dòng)畫你都理解了阅羹,那么補(bǔ)間動(dòng)畫就更好理解了:

通過代碼勺疼,和xml配置創(chuàng)建Animation

btnAnimation.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Animation animation = new TranslateAnimation(0, 200, 0, 200);
        animation.setDuration(1000);
        animation.setFillAfter(true);
        ivLogo.startAnimation(animation);
    }
});

btnXml.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Animation animation = AnimationUtils.loadAnimation(TweenActivity.this, R.anim.anim1);
        animation.setDuration(1000);
        animation.setFillAfter(true);
        ivLogo.startAnimation(animation);
    }
});

ivLogo.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d(TAG, "onClick");
    }
});

anim1.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator">

    <translate
        android:fromXDelta="200"
        android:fromYDelta="200"
        android:toXDelta="0"
        android:toYDelta="0" />

</set>

代碼很簡(jiǎn)單,看一下就明白了捏鱼,這里給 ivLogo添加了一個(gè)onclick事件执庐,是為了說明tween動(dòng)畫修改的不是view的真實(shí)屬性,怎么說呢导梆?如果點(diǎn)擊第一個(gè)動(dòng)畫轨淌,ivLogo的位置相對(duì)于原來的位置已經(jīng)偏離到 (+200, +200) 的位置,但是這個(gè)時(shí)候如果依然點(diǎn)擊 ivLog原位置看尼,依然會(huì)打印 "onClick", 所以其實(shí)View的位置屬性是沒有變化的递鹉。所以Tween動(dòng)畫修改的不是屬性值,而只是產(chǎn)生的一個(gè)動(dòng)畫效果而已藏斩。

效果圖:

test5.gif

學(xué)習(xí)Animation的話躏结,關(guān)注比較多的應(yīng)該是Animation的一些屬性設(shè)置,大家感興趣可以參考官網(wǎng)狰域。

FrameAnimation幀動(dòng)畫

上面兩個(gè)動(dòng)畫都與控件相關(guān)媳拴,但是幀動(dòng)畫就完全是圖片的疊加。原理和 gif 動(dòng)畫 或者電影一樣兆览,是因?yàn)槊恳粠^的速度太快屈溉,眼睛來不及反應(yīng)所以我們覺得是動(dòng)畫。

anim_frame.xml

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

    <item
        android:drawable="@drawable/logo1"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo2"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo3"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo4"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo5"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo6"
        android:duration="100" />
</animation-list>
  1. 根元素為 animation-list
  2. android:oneshot="false" 表示動(dòng)畫一直循環(huán)執(zhí)行

代碼調(diào)用:

btnStart.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ivWifi.setImageResource(R.drawable.anim_frame);
        AnimationDrawable animationDrawable = (AnimationDrawable) ivWifi.getDrawable();

        if (isRun) {
            animationDrawable.stop();
        } else {
            animationDrawable.start();
        }
        isRun = !isRun;
    }
});

一個(gè)全局變量表示動(dòng)畫是否在執(zhí)行抬探,然后點(diǎn)擊按鈕時(shí)更換狀態(tài)子巾。

效果圖:

test6.gif

總結(jié)

例子代碼下載鏈接:
http://download.csdn.net/detail/u013647382/9648454

Android中提供了3種動(dòng)畫,以后的趨勢(shì)還是屬性動(dòng)畫驶睦,所以大家不要猶豫砰左,快來鉆研屬性動(dòng)畫吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末场航,一起剝皮案震驚了整個(gè)濱河市缠导,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溉痢,老刑警劉巖僻造,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異孩饼,居然都是意外死亡髓削,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門镀娶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來立膛,“玉大人,你說我怎么就攤上這事”Ρ茫” “怎么了好啰?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長儿奶。 經(jīng)常有香客問我框往,道長,這世上最難降的妖魔是什么闯捎? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任椰弊,我火速辦了婚禮,結(jié)果婚禮上瓤鼻,老公的妹妹穿的比我還像新娘秉版。我一直安慰自己,他們只是感情好茬祷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布沐飘。 她就那樣靜靜地躺著,像睡著了一般牲迫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上借卧,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天盹憎,我揣著相機(jī)與錄音,去河邊找鬼铐刘。 笑死陪每,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的镰吵。 我是一名探鬼主播檩禾,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼疤祭!你這毒婦竟也來了盼产?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤勺馆,失蹤者是張志新(化名)和其女友劉穎戏售,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體草穆,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灌灾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悲柱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锋喜。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豌鸡,靈堂內(nèi)的尸體忽然破棺而出嘿般,到底是詐尸還是另有隱情段标,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布博个,位于F島的核電站怀樟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏盆佣。R本人自食惡果不足惜往堡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望共耍。 院中可真熱鬧虑灰,春花似錦、人聲如沸痹兜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽字旭。三九已至对湃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遗淳,已是汗流浹背拍柒。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屈暗,地道東北人拆讯。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像养叛,于是被迫代替她去往敵國和親种呐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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