前言
屬性動(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)畫