Android 動(dòng)畫總結(jié)
動(dòng)畫分類
Android中動(dòng)畫大概分為3類:
- TweenAnimation(補(bǔ)間動(dòng)畫)
- TranslateAnimation
- ScaleAnimation
- RotateAnimation
- AlphaAnimation
- FrameAnimation(幀動(dòng)畫)
- PropertyAnimation(屬性動(dòng)畫)
PropertyAnimation屬性動(dòng)畫
先來看屬性動(dòng)畫坡贺,這是潮流官辈,也是google推薦使用的。
屬性動(dòng)畫和一般的View動(dòng)畫的重要區(qū)別在于:
- 屬性動(dòng)畫控制的是實(shí)實(shí)在在的屬性遍坟,但是View動(dòng)畫只是產(chǎn)生一個(gè)動(dòng)畫拳亿,并沒有改變控件的屬性。
- 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();
效果圖:
打印出來的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,表示不透明酪我,否則我們的控件就看不見了消痛。
效果圖:
使用xml文件配置屬性動(dòng)畫
android的套路,一定會(huì)允許你使用xml的方式來配置動(dòng)畫都哭,所以我們看看怎么配置:
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();
效果圖:
使用插值器
我們知道 ValueAnimator就是用來產(chǎn)生一些動(dòng)畫屬性中間值欺矫,但是默認(rèn)是均勻變化的纱新,插值器就是要使得產(chǎn)生的中間序列非均勻化,當(dāng)然不僅是非均勻這么簡(jiǎn)單汇陆,還有很多特效:
下面是官網(wǎng)提供的一些插值器怒炸,大家都可以試試,我這里只是拋磚引玉毡代,展示最簡(jiǎn)單的用法:
使用方法:
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());
效果圖:
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)畫效果而已藏斩。
效果圖:
學(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>
- 根元素為 animation-list
- 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)子巾。
效果圖:
總結(jié)
例子代碼下載鏈接:
http://download.csdn.net/detail/u013647382/9648454
Android中提供了3種動(dòng)畫,以后的趨勢(shì)還是屬性動(dòng)畫驶睦,所以大家不要猶豫砰左,快來鉆研屬性動(dòng)畫吧!