Android 3.0之前已有View動(dòng)畫框架Animation(詳見:Android之視圖動(dòng)畫Animation)毕源,但存在一些局限性,當(dāng)某個(gè)元素發(fā)生視圖動(dòng)畫后鞋喇,其響應(yīng)事件位置還在動(dòng)畫前的地方。于是3.0之后,Google提出了屬性動(dòng)畫徙鱼。
Animation的優(yōu)點(diǎn)
1. 版本兼容
不得不說,相對于 Animation,Animator 的版本兼容性還是太差,直到 Android3.0才開始出現(xiàn)的 Animator, 是無法滿足目前開發(fā)環(huán)境2.x 的兼容支持的,而且在 android 官方的 support 包中也沒有對于低版本的 Animator 進(jìn)行支持,所以單從版本兼容來看, Animator 還是不夠的,不過這是系統(tǒng)歷史原因,我們只能接受.
2.實(shí)現(xiàn)效率
同樣的,這也是 Animator 的一個(gè)缺點(diǎn),由于 Animator 是直接通過設(shè)置對象的 setter,getter 方法,來起到動(dòng)畫顯示效果的,所以為了滿足對任意對象調(diào)用正確方法,Animator 使用了 Java 反射機(jī)制, 而 Animation 則是直接通過代碼對矩陣進(jìn)行處理,所以就效率這一方面而言, Animator比不上 Animation
Animator的實(shí)現(xiàn)原理
Animator 動(dòng)畫的實(shí)現(xiàn)機(jī)制說起來其實(shí)更加簡單一點(diǎn),因?yàn)樗鋵?shí)只是計(jì)算動(dòng)畫開啟之后,結(jié)束之前,到某個(gè)時(shí)間點(diǎn)得時(shí)候,某個(gè)屬性應(yīng)該有的值,然后通過回調(diào)接口去設(shè)置具體值,其實(shí) Animator 內(nèi)部并沒有針對某個(gè) view 進(jìn)行刷新,來實(shí)現(xiàn)動(dòng)畫的行為,動(dòng)畫的實(shí)現(xiàn)是在設(shè)置具體值的時(shí)候,方法內(nèi)部自行調(diào)取的類似 invalidate 之類的方法實(shí)現(xiàn)的.也就是說,使用 Animator ,內(nèi)部的屬性發(fā)生了變化.
Animator的優(yōu)點(diǎn)
1. 適用性
在上一個(gè)分析中,我們看到了由于 Animator 使用了反射機(jī)制導(dǎo)致其效率偏低,但是這也帶來了他適用的對象范圍的增加, Animation 僅對 View 這一種對象有用,但是 Animator 可以設(shè)置任意對象的屬性,使其在某段時(shí)間內(nèi)進(jìn)行變化
2. 使用效果
相信大家平時(shí)使用 Animation 的時(shí)候,都有發(fā)現(xiàn)當(dāng)正在進(jìn)行平移移動(dòng),或者動(dòng)畫結(jié)束后,但位置發(fā)生改變的時(shí)候,你點(diǎn)擊之前的位置,點(diǎn)擊效果仍然存在,這就是因?yàn)?View 在內(nèi)部的坐標(biāo)位置其實(shí)沒有發(fā)生改變,而如果使用 Animator 進(jìn)行位移變換,那么你的點(diǎn)擊位置就會(huì)隨著動(dòng)畫效果發(fā)生相應(yīng)改變,所以即使你正處在動(dòng)畫過程中,你也可以去點(diǎn)擊按鈕得到你想要的效果.
-----------------------------------------------
補(bǔ)間動(dòng)畫
- 視圖動(dòng)畫,也叫Tween(補(bǔ)間)動(dòng)畫可以在一個(gè)視圖容器內(nèi)執(zhí)行一系列簡單變換(位置针姿、大小袱吆、旋轉(zhuǎn)、透明度)距淫。譬如绞绒,如果你有一個(gè)TextView對象,您可以移動(dòng)榕暇、旋轉(zhuǎn)蓬衡、縮放、透明度設(shè)置其文本拐揭,當(dāng)然撤蟆,如果它有一個(gè)背景圖像,背景圖像會(huì)隨著文本變化堂污。
- 補(bǔ)間動(dòng)畫通過XML或Android代碼定義家肯,建議使用XML文件定義,因?yàn)樗呖勺x性盟猖、可重用性讨衣。
如下是視圖動(dòng)畫相關(guān)的類繼承關(guān)系:
AlphaAnimation <alpha> 放置在res/anim/目錄下 漸變透明度動(dòng)畫
RotateAnimation <rotate> 放置在res/anim/目錄下 畫面轉(zhuǎn)移旋轉(zhuǎn)動(dòng)果
ScaleAnimation <scale> 放置在res/anim/目錄下 漸變尺寸伸縮動(dòng)畫
TranslateAnimation <translate> 放置在res/anim/目錄下 畫面轉(zhuǎn)換位置移動(dòng)動(dòng)畫效果
AnimationSet <set> 放置在res/anim/目錄下 一個(gè)持有其它動(dòng)畫元素alpha、scale式镐、translate反镇、rotate或者其它set元素的容器
Animation屬性詳解
android:detachWallpaper setDetachWallpaper(boolean) 是否在壁紙上運(yùn)行
android:duration setDuration(long) 動(dòng)畫持續(xù)時(shí)間,毫秒為單位
android:fillAfter setFillAfter(boolean) 控件動(dòng)畫結(jié)束時(shí)是否保持動(dòng)畫最后的狀態(tài)
android:fillBefore setFillBefore(boolean) 控件動(dòng)畫結(jié)束時(shí)是否還原到開始動(dòng)畫前的狀態(tài)
android:fillEnabled setFillEnabled(boolean) 與android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 設(shè)定插值器(指定的動(dòng)畫效果娘汞,譬如回彈等)
android:repeatCount setRepeatCount(int) 重復(fù)次數(shù)
android:repeatMode setRepeatMode(int) 重復(fù)類型有兩個(gè)值歹茶,reverse表示倒序回放,restart表示從頭播放
android:startOffset setStartOffset(long) 調(diào)用start函數(shù)之后等待開始運(yùn)行的時(shí)間你弦,單位為毫秒
android:zAdjustment setZAdjustment(int) 表示被設(shè)置動(dòng)畫的內(nèi)容運(yùn)行時(shí)在Z軸上的位置(top/bottom/normal)惊豺,默認(rèn)為normal
LinearLayout layout = (LinearLayout) findViewById(R.id.test_layout );
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1,0,1);
scaleAnimation.setDuration(2000);
LayoutAnimationController layoutAnimationController = new LayoutAnimationController(scaleAnimation,0.5f);
layoutAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
layout .setLayoutAnimation(layoutAnimationController);
AnimationSet詳解
AnimationSet繼承自Animation,是上面四種的組合容器管理類禽作,沒有自己特有的屬性尸昧,他的屬性繼承自Animation,所以特別注意旷偿,當(dāng)我們對set標(biāo)簽使用Animation的屬性時(shí)會(huì)對該標(biāo)簽下的所有子控件都產(chǎn)生影響烹俗。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
<set>
...
</set>
</set>
至于補(bǔ)間動(dòng)畫的使用爆侣,Animation還有如下一些比較實(shí)用的方法介紹逐幀動(dòng)畫
屬性動(dòng)畫
ObjectAnimator
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "translationX", 300);
objectAnimator1.setInterpolator(new AccelerateInterpolator());
objectAnimator1.setDuration(2000);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);//Animation.INFINITE 表示重復(fù)多次
objectAnimator.setRepeatMode(ValueAnimator.RESTART);//RESTART表示從頭開始,REVERSE表示從末尾倒播
objectAnimator1.start();
Animation 使用setFillAfter(true)讓動(dòng)畫停留在原處
動(dòng)畫屬性值
平移:
translationX和translationY:增量控制view從它布局容器左上角坐標(biāo)偏移
ObjectAnimator.ofFloat(imageView, "translationX", 300f);
rotation 控制view旋轉(zhuǎn):
rotation幢妄、rotationX兔仰、rotationY:控制view繞支點(diǎn)進(jìn)行旋轉(zhuǎn)
ObjectAnimator.ofFloat(imageView, "rotation", 360);
// 翻轉(zhuǎn)
ObjectAnimator rotateX = ObjectAnimator.ofFloat(mTvText, "rotationX", 0, 360);
rotateX.setDuration(2000);
rotateX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 屬性動(dòng)畫執(zhí)行時(shí),會(huì)回調(diào)這個(gè)監(jiān)聽器
// 如果你的屬性沒有觸發(fā)重繪,那你可以在這里加上invalidate()或postInvalidate()
Log.d(TAG, "onAnimationUpdate: value=" + animation.getAnimatedValue()
+ "fraction=" + animation.getAnimatedFraction());
}
});
scaleX、scaleY:控制view進(jìn)行縮放:
scaleX磁浇、scaleY:控制view繞支點(diǎn)進(jìn)行縮放
ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.3f斋陪,1f);
alpha:控制view透明度:
alpha:控制view透明度,默認(rèn)是1(不透明)置吓,0完全透明(不可見)
ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0.3f);
可變數(shù)組參數(shù)
可以有一個(gè)到N個(gè),如果是一個(gè)值的話默認(rèn)這個(gè)值是動(dòng)畫過渡值的結(jié)束值缔赠。如果有N個(gè)值衍锚,動(dòng)畫就在這N個(gè)值之間過渡。
動(dòng)畫監(jiān)聽:
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "alpha", 0.5f, 1f);
objectAnimator1.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
Android提供了AnimatorListenerAdapter:
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "alpha", 0.5f, 1f);
objectAnimator1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
XML中使用 ObjectAnimator
放置在res/animator/目錄下
<objectAnimator>屬性解釋:
XML屬性動(dòng)畫使用方法:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.animtor.property_animator);
set.setTarget(myObject);
set.start();
ValueAnimator
ValueAnimator 本身不提供任何動(dòng)畫效果嗤堰,像個(gè)數(shù)值 發(fā)生器戴质,用來產(chǎn)生具有一點(diǎn)規(guī)律數(shù)字。
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 500);
valueAnimator.setTarget(mTvText);
valueAnimator.setDuration(2000);
/**
* valueAnimator 必須使用updateListener進(jìn)行接收數(shù)據(jù)
* ValueAnimator不跟屬性進(jìn)行綁定
*/
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 在數(shù)值變化監(jiān)聽器中實(shí)現(xiàn)動(dòng)畫變換
mTvText.setTranslationX((Float) animation.getAnimatedValue());
}
});
valueAnimator.start();
PropertyValuesHolder 針對同一個(gè)對象多個(gè)屬性踢匣,同時(shí)作用多種動(dòng)畫
PropertyValuesHolder propertyValuesHolder1 = PropertyValuesHolder.ofFloat("translationX", 300f);
PropertyValuesHolder propertyValuesHolder2 = PropertyValuesHolder.ofFloat("alpha", 1f, 0.5f);
PropertyValuesHolder propertyValuesHolder3 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder propertyValuesHolder4 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(imageView, propertyValuesHolder1, propertyValuesHolder2, propertyValuesHolder3, propertyValuesHolder4)
.setDuration(5000).start();
AnimatorSet
與PropertyValuesHolder類似告匠,但AnimatorSet多了playTogether(同時(shí)執(zhí)行)、playSequentially(順序執(zhí)行)离唬、play(objectAnimator1).with(objectAnimator2)后专、before、after這些方法協(xié)同工作输莺。
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0.5f);
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView, "translationY", 300);
ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(5000);
animatorSet.playTogether(objectAnimator1, objectAnimator2,objectAnimator3);
animatorSet.start();
AnimatorSet set = new AnimatorSet(); // 下面代碼產(chǎn)生的規(guī)則并不能確定anim2與anim3的先后關(guān)系
// 下面代碼產(chǎn)生的規(guī)則可間接確定anim2與anim3的先后關(guān)系
set.play(anim1).before(anim2).before(anim3);
// 下面代碼產(chǎn)生的規(guī)則可完全確定anim1戚哎、anim2、anim3之間的先后關(guān)系
set.play(anim1).before(anim2).after(anim3);
set.play(anim1).before(anim2);
set.play(anim2).before(anim3);
xml使用屬性動(dòng)畫
- res下建立animator文件夾嫂用,然后建立res/animator/main_animator.xml
- set標(biāo)簽型凳,有一個(gè)orderring屬性設(shè)置為together,還有另一個(gè)值:sequentially(表示一個(gè)接一個(gè)執(zhí)行)嘱函。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:valueType="floatType">
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="0.5" />
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="0.5" />
</set>
<!-- 代碼中調(diào)用
Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), R.animator.set_animator);
animator.setTarget(imageView);
animator.start();
-->
View的animate方法
Android 3.0后甘畅,谷歌給View增加animate方法直接驅(qū)動(dòng)屬性動(dòng)畫。
imageView.animate()
.alpha(0.5f)
.y(300)
.setDuration(2000)
//api min is 16
.withStartAction(new Runnable() {
@Override
public void run() {
}
})
//api min is 16
.withEndAction(new Runnable() {
@Override
public void run() {
}
})
.start();