屬性動畫實際上是一種不斷地對值進行操作的機制寞蚌,并將值賦值到指定對象的指定屬性上,可以是任意對象的任意屬性允耿。屬性動畫機制已經(jīng)不只是針對于View來設計的了奕坟,也不限定于只能實現(xiàn)移動、縮放罗侯、旋轉和淡入淡出這幾種動畫操作器腋,同時也不再只是一種視覺上的動畫效果了。所以我們?nèi)匀豢梢詫⒁粋€View進行移動或者縮放钩杰,但同時也可以對自定義View中的Point對象進行動畫操作了纫塌。我們只需要告訴系統(tǒng)動畫的運行時長,需要執(zhí)行哪種類型的動畫讲弄,以及動畫的初始值和結束值措左,剩下的工作就可以全部交給系統(tǒng)去完成了。
-
ValueAnimator
ValueAnimator是整個屬性動畫機制當中最核心的一個類避除,屬性動畫的運行機制是通過不斷地對值進行操作來實現(xiàn)的怎披,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。它的內(nèi)部使用一種時間循環(huán)的機制來計算值與值之間的動畫過渡瓶摆,我們只需要將初始值和結束值提供給ValueAnimator凉逛,并且告訴它動畫所需運行的時長,那么ValueAnimator就會自動幫我們完成從初始值平滑地過渡到結束值這樣的效果群井。除此之外状飞,ValueAnimator還負責管理動畫的播放次數(shù)、播放模式书斜、以及對動畫設置監(jiān)聽器等诬辈,確實是一個非常重要的類。
示例:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.start()
setStartDelay()方法來設置動畫延遲播放的時間
setRepeatCount()設置動畫循環(huán)播放的次數(shù)
setRepeatMode()方法設置循環(huán)播放的模式菩佑,循環(huán)模式包括RESTART和REVERSE兩種自晰,分別表示重新播放和倒序播放的意思凝化。
-
ObjectAnimator
ObjectAnimator是經(jīng)常接觸到的類稍坯,它是繼承自ValueAnimator的,底層的動畫實現(xiàn)機制也是基于ValueAnimator來完成的。提供了ofInt瞧哟、ofFloat混巧、ofObject。
示例:
float curTranslationX = textview.getTranslationX();
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);
animator.setDuration(5000);
animator.start();
調(diào)用了TextView的getTranslationX()方法來獲取到當前TextView的translationX的位置勤揩,然后ofFloat()方法的第二個參數(shù)傳入"translationX"咧党,緊接著后面三個參數(shù)用于告訴系統(tǒng)TextView應該怎么移動。ofFloat()方法的第二個參數(shù)可以是
- alpha:表示View對象的透明度
- rotation陨亡、rotationX傍衡、rotationY:控制View對象圍繞支點進行2D和3D旋轉
- translationX、translationY:作為一種增量來控制著View對象從它布局容器的左上角坐標開始的位置负蠕。
- x蛙埂、y:描述了View對象在它的容器中的最終位置,是最初的左上角坐標和translationX和translationY值得累加
- scaleY遮糖、scaleX:控制View對象圍繞它的支點進行2D縮放
ObjectAnimator內(nèi)部的工作機制并不是直接對傳入的屬性名進行操作的绣的,而是會去尋找這個屬性名對應的get和set方法,這兩個方法是由View對象提供的欲账,因此alpha屬性所對應的get和set方法應該就是:
public void setAlpha(float value);
public float getAlpha();
-
組合動畫
實現(xiàn)組合動畫功能主要需要借助AnimatorSet這個類屡江,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)將會返回一個AnimatorSet.Builder的實例赛不,AnimatorSet.Builder中包括以下四個方法:- after(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之后執(zhí)行
- after(long delay) 將現(xiàn)有動畫延遲指定毫秒后執(zhí)行
- before(Animator anim) 將現(xiàn)有動畫插入到傳入的動畫之前執(zhí)行
- with(Animator anim) 將現(xiàn)有動畫和傳入的動畫同時執(zhí)行
示例:
TextView先從屏幕外移動進屏幕惩嘉,然后開始旋轉360度,旋轉的同時進行淡入淡出操作俄删。
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textviwe, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
-
Animator監(jiān)聽器
Animator類當中提供了一個addListener()方法宏怔,這個方法接收一個AnimatorListener,我們只需要去實現(xiàn)這個AnimatorListener就可以監(jiān)聽動畫的各種事件了畴椰。
ValueAnimator臊诊、ObjectAnimator、AnimatorSet都是繼承自Animator的斜脂,都可以使用addListener()這個方法抓艳。
添加一個監(jiān)聽器的代碼如下:
anim.addListener(new AnimatorListener(){
@Override
public void onAnimationStart(Animator animator){ //在動畫開始的時候調(diào)用
}
@Override
public void onAnimationRepeat(Animator animator){ //動畫重復執(zhí)行的時候調(diào)用
}
@Override
public void onAnimationEnd(Animator animator){ //在動畫結束的時候調(diào)用
//刪除動畫
Log.e(TAG, "onAnimationEnd");
ViewGroup parent = (ViewGroup) mBlueBall.getParent();
if (parent != null)
parent.removeView(mBlueBall);
}
@Override
public void onAnimationCancel(Animator animator){ //在動畫被取消的時候調(diào)用
}
});
有時并不用將這四個接口都實現(xiàn),Android提供了一個適配器類帚戳,叫作AnimatorListenerAdapter玷或,使用這個類就可以解決掉實現(xiàn)接口繁瑣的問題了,如下所示:
anim.addListener(new AnimatorListenerAdapter(){
});
向addListener()方法中傳入這個適配器對象片任,由于AnimatorListenerAdapter中已經(jīng)將每個接口都實現(xiàn)好了偏友,所以這里不用實現(xiàn)任何一個方法也不會報錯。因此对供,如果想監(jiān)聽動畫結束這個事件位他,就只需要單獨重寫這一個方法就可以了氛濒,如下所示:
anim.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animator){ //在動畫結束的時候調(diào)用
}
});
-
使用XML編寫動畫
通過XML來編寫動畫在重用方面將會變得非常輕松,比如某個通用的動畫編寫到XML里面鹅髓,可以在各個界面當中輕松去重用它舞竿。
如果想要使用XML來編寫動畫,首先要在res目錄下面新建一個animator文件夾窿冯,所有屬性動畫的XML文件都應該存放在這個文件夾當中骗奖。然后在XML文件中我們一共可以使用如下三種標簽:
<animator> 對應代碼中的ValueAnimator
<objectAnimator> 對應代碼中的ObjectAnimator
<set> 對應代碼中的AnimatorSet
示例:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1" **
android:valueTo="0" **
android:valueType="floatType" **
android:propertyName="alpha"/>**
使用XML來完成復雜的組合動畫操作:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<objectAnimator
andorid:duration="2000"
android:propertyName="translationX"
android:valueFrom="-500"
andorid:valueTo="0"
android:valueType="floatType"
/>
<set android:ordering="together"
<objectAnimator
andorid:duration="2000"
android:propertyName="rotation"
android:valueFrom="0"
andorid:valueTo="360"
android:valueType="floatType"
/>
<set android:ordering="sequentially">
<objectAnimator
andorid:duration="2000"
android:propertyName="alpha"
android:valueFrom="1"
andorid:valueTo="0"
android:valueType="floatType"
/>
<objectAnimator
andorid:duration="2000"
android:propertyName="alpha"
android:valueFrom="0"
andorid:valueTo="1"
android:valueType="floatType"
/>
</set>
</set>
</set>
使用set標簽,orderring屬性設置為together醒串,sequentially表示一個接一個執(zhí)行
在代碼中把文件加載進來并將動畫啟動:
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
調(diào)用AnimatorInflater的loadAnimator來將XML動畫文件加載進來执桌,然后再調(diào)用setTarget()方法將這個動畫設置到某一個對象上面,最后再調(diào)用start()方法啟動動畫芜赌。
-
View的animate()方法
animate()方法是屬性動畫的一種簡寫方式
view.animate()
.alpha(0)
.y(300)
.setDuration(300)
.withStartAction(new Runnable() {
@Override
public void run(){
}
})
.withEndAction(new Runnable() {
@Override
public void run(){
runOnUIThread(new Runnable(){
@Override
public void run(){
}
});
}
}).start();
-
ValueAnimator的高級用法
如果有一個自定義的View鼻吮,在這個View當中有一個Point對象用于管理坐標,然后在onDraw()方法當中就是根據(jù)這個Point對象的坐標值來進行繪制的较鼓。也就是說椎木,如果我們可以對Point對象進行動畫操作,那么整個自定義View的動畫效果就有了博烂。
TypeEvaluator的作用就是告訴動畫系統(tǒng)如何從初始值過度到結束值香椎。ValueAnimator.ofFloat()方法就是實現(xiàn)了初始值與結束值之間的平滑過度,就是因為系統(tǒng)內(nèi)置了一個FloatEvaluator禽篱,它通過計算告知動畫系統(tǒng)如何從初始值過度到結束值畜伐,我們來看一下FloatEvaluator的代碼實現(xiàn):
public class FloatEvaluator implements TypeEvaluator{
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction*(((Number) endValue).floatValue() - startFloat);
}
}
loatEvaluator實現(xiàn)了TypeEvaluator接口,然后重寫evaluate()方法躺率。evaluate()方法當中傳入了三個參數(shù)玛界,第一個參數(shù)fraction用于表示動畫的完成度的,根據(jù)它來計算當前動畫的值應該是多少悼吱,第二第三個參數(shù)分別表示動畫的初始值和結束值慎框。
-
對象的動畫操作
因為系統(tǒng)完全無法知道如何從初始對象過度到結束對象,需要實現(xiàn)一個自己的TypeEvaluator來告知系統(tǒng)如何進行過度后添。
public class Point {
private float x;
private float y;
public Point(float x, float y){
this.x = x;
this.y = y;
}
public float getX(){
return x;
}
public float getY(){
reutrn y;
}
}
Point類只有x和y兩個變量用于記錄坐標的位置笨枯,接下來定義PointEvaluator:
public valss PointEvaluator implements TypeEvaluator{
@Overrride
public Object evaluate(float fraction, Object startValue, Object endValue){
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint + fraction*(endPoint.getX() - startPoint.getX());
float y = startPoint + fraction*(endPoint.getY() - startPoint.getY());
Point point = new Point(x, y);
reutrn point;
}
}
PointEvaluator同樣實現(xiàn)了TypeEvaluator接口并重寫了evaluate()方法。在evaluate()方法中的先將startValue和endValue強制轉為Point對象遇西,然后根據(jù)fraction來計算當前動畫的x和y的值馅精,然后封裝到一個新的Point對象返回。
對Point對象進行動畫操作:
新建一個MyAninView繼承自View:
public class MyAnimView extends View{
public static final float RADIUS = 50f;
private Point currentPoint;
private Paint mPaint;
public MyAnimView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//用于繪制時消除鋸齒
mPaint.setColor(Color.BULE);
}
@Override
protected void OnDraw(Canvas canvas){
if (currentPoint == null){
currentPoint = new Point(RADIUS , RADIUS );
drawCircle(canvas);
startAnimation();
} else{
drawCircle(canvas);
}
}
private void drawCircle(Canvas canvas){
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
private void startAnimation() {
Point startPoint = new Point(RADIUS, RADIUS);
Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
valueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation){
currentPoint = (Point) animation.getAnimateValue();
invalidate(); //請求重新draw()粱檀,但只會繪制調(diào)用者本身
}
});
anim.setDuration(5000);
anim.start();
}
}
首先在自定義View的構造方法當中初始化了一個Paint對象作為畫筆洲敢,并將畫筆顏色設置為藍色,接著在onDraw()方法當中進行繪制。這里我們繪制的邏輯是由currentPoint這個對象控制的彻磁,如果currentPoint對象不等于空,那么就調(diào)用drawCircle()方法在currentPoint的坐標位置畫出一個半徑為50的圓傅蹂,如果currentPoint對象是空哮塞,那么就調(diào)用startAnimation()方法來啟動動畫。
-
布局動畫
布局動畫是指作用在ViewGroup上凳谦,給ViewGroup增加View時添加一個動畫過度效果忆畅。
最簡單的布局動畫是在IViewGroup的XML中,當ViewGroup添加View時尸执,子View會呈現(xiàn)逐漸顯示Android默認的過渡效果家凯,:
android:animateLayoutChanges="true"
可以通過LayoutAnimationController類來自定義一個子View的過渡效果:
Linearlayout ll = (LinearLayout) findViewById(R.id.ll);
//設置過渡動畫
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(2000);
//設置布局動畫的顯示屬性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//為ViewGroup設置布局動畫
ll.setLayoutAnimation(lac);
通過以上代碼,給LinearLayout增加了一個視圖動畫如失,讓子View在出現(xiàn)的時候绊诲,有一個縮放的動畫效果。
LayoutAnimationController構造函數(shù)的第一個參數(shù)是需要作用的動畫褪贵,第二個參數(shù)是每個子View顯示的delay時間掂之。當delay不為0時,可以設置子View的順序:
- LayoutAnimationController.ORDER_NORMAL
- LayoutAnimationController.ORDER_RANDOM 隨機
- LayoutAnimationController.ORDER_REVERSE 反序
-
Interpolator插值器
這個方法主要是用來控制android動畫的執(zhí)行速率脆丁,可以使存在的動畫效果: - AccelerateInterpolator ——在動畫開始的地方速率改變比較慢世舰,然后開始加速
- AnticipateInterpolator ——開始的時候向后然后向前甩
- AnticipateOvershootInterpolator ——開始的時候向后然后向前甩一定值后返回最后的值
- BounceInterpolator ——開始時彈出,動畫結束的時候彈起
- CycleInterpolator ——動畫循環(huán)播放特定的次數(shù)槽卫,速率改變沿著正弦曲線
- DecelerateInterpolator—— 在動畫開始的地方快然后慢
- LinearInterpolator ——以常量速率改變
- OvershootInterpolator ——向前甩一定值后再回到原來位置