前言
屬性動畫是API11加入的新特性,旨在實(shí)現(xiàn)更加絢麗的動畫效果,不再像 View動畫那樣只能支持四種簡單的變換,其可以對任意對象的屬性進(jìn)行動畫而不僅僅局限于View。
由于屬性動畫從API11才加入,我們可以采用nineoldandroids開源動畫庫來兼容以前版本,Nineoldandroids的功能和系統(tǒng)原生的android.animation.*中類的功能完全一致,使用方法也完全一樣,只要我們用nineoldandroids 來編寫動畫,就可以在所有的Android系統(tǒng)上運(yùn)行。
屬性動畫的使用
下面來集中學(xué)習(xí)屬性動畫中的幾個常用的動畫類:
- ValueAnimator
- ObjectAnimator 其繼承自 ValueAnimator
- AnimatorSet 動畫集合
1. ValueAnimator
關(guān)于ValueAnimator,首先先要理解一個概念,ValueAnimator 的動畫并不是直接作用于對象的,而是對一個值做動畫,然后我們通過監(jiān)聽這個值得變化,來完成我們對所要變換的對象的屬性值的修改,從而實(shí)現(xiàn)動畫效果。下面通過一個例子,可能會更好理解:
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)前動畫進(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)聽這個變換過程,在onAnimationUpdate 方法中,通過獲取動畫的變換進(jìn)度來最終確定變換結(jié)果,并完成對 對象屬性值的修改。
2. ObjectAnimator
ObjectAnimator 繼承自 ValueAnimator , 并做了新的封裝。ObjectAnimator 允許指定 作用的對象以及變換的屬性,我們只需要調(diào)用其提供的方法,剩下復(fù)雜的變換過程,交給ObjectAnimator來完成即可。
在使用ObjectAnimator之前,我們先來看一下幾個常用的View屬性成員:
- translationX,translationY:X/Y 軸方向偏移量的變化,相對于view 自身。
- rotationX,rotationY:沿 X/Y軸方向旋轉(zhuǎn)。
- scaleX,scaleY:沿 X/Y 軸縮放。
- x,y:改變view在整個屏幕上的絕對坐標(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);
從上面這個例子來看,target,propertyName 很好理解,分別是 作用的對象及 對象的屬性名。 重點(diǎn)來說一下 , values。values 是可以設(shè)置多個的,如下所示:
ObjectAnimator.ofFloat(view, "translationY", 100).start();
ObjectAnimator.ofFloat(view, "translationY", 100,150,200).start();
當(dāng)只設(shè)置一個時(shí)就把通過getTranslationY()反射獲取的值作為初始值,設(shè)置的值作為終點(diǎn);如果設(shè)置兩個或多個,那么就會不斷的改變屬性值,而初始值則為 第一個屬性值。
ObjectAnimator是通過不斷地調(diào)用setXXX方法更新屬性值來達(dá)到動畫的效果的,這就要求Object必須聲明有g(shù)etXXX和setXXX方法。
**在ObjectAnimator 的使用過程中,總會遇到 設(shè)置的屬性名 在Object 中并不存在,或者沒有提供getXXX和setXXX方法,再或者提供了方法并不能實(shí)現(xiàn)預(yù)期的效果等這類問題。那么下面提供一個解決方法: 我們可以自定義一個類來包裝 目標(biāo)對象,并提供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í)修改多個屬性怎么辦呢? 別急,這個時(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 表示 動畫進(jìn)度 value 表示動畫播放到當(dāng)前進(jìn)度的參數(shù)值
public static Keyframe ofFloat(float fraction, float value)
3. AnimationSet
AnimationSet 是動畫集合,可以定義一組動畫,使用起來也是極其簡單的。
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, ...); //多個動畫同時(shí)執(zhí)行
animSet.play(objectAnimatorA).after(objectAnimatorB); //多個動畫先后執(zhí)行
animSet.start();
4. 屬性動畫監(jiān)聽器
這里提一下,屬性動畫提供了兩個接口,用于監(jiān)聽動畫的播放過程。
- AnimatorUpdateListener
監(jiān)聽動畫整個播放過程
objectAnimatorA.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator arg0) {
// TODO Auto-generated method stub
}
});
- AnimatorListener
用于監(jiān)聽動畫的開始,結(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)動畫的非勻速變化,比較常見的插值器(Interpolator)有 LinearInterpolator (線性插值器:勻速動畫),AcclerateDecelerateInterpolator(加減速插值器:兩頭慢中間快)和 DecelerateInterpolator(減速插值器:動畫速度越來越慢)。
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
2. 估值器(TypeEvluator)
估值器用于 計(jì)算變換后的屬性值。通過動畫進(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í)就是一個簡單的估值算法。使用方式在 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)前動畫進(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();
}
}
//...省略了很多代碼
}
屬性動畫的特殊使用
通過屬性動畫 為 ViewGroup 的子元素設(shè)置動畫,相對于View 動畫 ,有了更多的選擇。
如果想開啟默認(rèn)的效果只需要在布局文件中添加:
android:animateLayoutChanges=”true”
如果想實(shí)現(xiàn)更炫的效果,就需要用到LayoutTransition,其有5種動畫狀態(tài)可以編輯:
- LayoutTransition.APPEARING
表示 View出現(xiàn)時(shí) 自身需要的動畫效果 - LayoutTransition.DISAPPEARING
表示 View消失時(shí) 自身需要的動畫效果 - LayoutTransition.CHANGE_APPEARING
表示 View出現(xiàn)時(shí) 整個容器所有View需要的動畫效果 - LayoutTransition.CHANGE_DISAPPEARING
表示 View消失時(shí) 整個容器所有View消失的動畫效果 - LayoutTransition.CHANGE
表示 除了以上這些情況,整個容器View發(fā)生改變時(shí)所需要的動畫效果
ok,基礎(chǔ)就說這個多,下面來看一下具體使用:
item動畫效果的設(shè)置:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "alpha", 0F, 1F).
setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);
容器動畫效果的設(shè)置:
//pvhLeft, pvhTop, pvhRight, pvhBottom 必須添加,否則不顯示動畫效果
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)于容器動畫的設(shè)置,pvhLeft, pvhTop, pvhRight, pvhBottom 必須添加,不然添加不了動畫效果,至于具體原因,大家可以自行學(xué)習(xí),實(shí)際上我也不知道 >_<。
最后給留一個demo ,LayoutTransition動畫的實(shí)現(xiàn):
LayoutTransition動畫的具體實(shí)現(xiàn)效果
總結(jié)
寫到這里,屬性動畫的基本使用就差不多了,我在這里并沒有涉及屬性動畫通過 xml定義的方式,總感覺屬性動畫通過代碼實(shí)現(xiàn)會更簡單清晰一些,如果大家有興趣可以去學(xué)習(xí)一下。
動畫其他:View動畫和幀動畫