Android 動畫之屬性動畫(三)#
Animation Listeners##
前面的兩節中都或多或少提到了動畫監聽 Animation Listeners 這個概念。它用來監聽動畫的過程,并且提供一些接口讓你實現一些邏輯。比如說你想在動畫開始的時候進行一些操作,就需要為這個動畫設置一個監聽器,然后實現你的邏輯,這樣,當監聽器監聽到動畫開始,就回去實現你的邏輯
Animator.AnimatorListener
Animator 類當中提供了一個 addListener() 方法,這個方法接收一個接口類 AnimatorListener ,獲得該接口后就可以重寫以下四個方法:
- onAnimationStart() - 當動畫開始的時候調用
- onAnimationEnd() - 當動畫結束后調用
- onAnimationRepeat() - 當動畫重復播放時調用
- onAnimationCancel() - 當動畫被取消的時候調用,并且在調用完這個方法后,會自動調用 onAnimationEnd() 方法
下面的例子中為每一段代碼實現了一個邏輯:彈出對應的文字
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
Toast.makeText(MainActivity.this, "start", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationEnd(Animator animator) {
Toast.makeText(MainActivity.this, "End", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationCancel(Animator animator) {
Toast.makeText(MainActivity.this, "Cancel", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationRepeat(Animator animator) {
Toast.makeText(MainActivity.this, "rapeat", Toast.LENGTH_LONG).show();
}
});
anim.start();
//添加一個按鈕,按鈕的功能用于cancel動畫
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
anim.cancel();
}
});
來看運行后效果
按下cancel鍵后
當然如果你不想把所有方法都重寫的話,可以使用 AnimatorListenerAdapter 類,如下
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
}
});
這樣的話就可以選擇自己想要重寫方法進行重寫
ValueAnimator.AnimatorUpdateListener
Animator 類當中提供了一個 addUpdateListener() 方法,這個方法接收一個接口類 AnimatorUpdateListener ,獲得該接口后就可以重寫 onAnimationUpdate() 方法,這個方法為動畫提供每一幀的監聽,之前在(一)的時候提到,每一幀其實是用一個 value 值來控制的,所以通常情況下我們都需要在每一幀監聽的時候獲得這個 value 值,就需要調用 ValueAnimator.getAnimatedValue() 方法來獲得。
ValueAnimator vanim = ValueAnimator.ofInt(0,10,20);
vanim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//如果之前的ValueAnimtor指定的是Int的i話,那么返回的Value就是int類型,
也就是返回值類型與你創建的類型一致
int value = (int) valueAnimator.getAnimatedValue();
}
});
Animating Layout Changes to ViewGroups##
屬性動畫不僅可以讓你對 view 類進行動畫操作,同時它還可以用于 viewgroup 類的變化進行動畫操作。比如說你有一個 gridLayout ,你需要在里面添加按鈕,那么按鈕加到 gridLayout 是一個動畫的過程,系統會有一個默認的動畫。如果想要改變這個默認的動畫效果,就可以借助 LayoutTransition 類來為這個 gridLayout 修改動畫效果。使用步驟:
- 新建一個 LayoutTransition
- 對 LayoutTransition 通過調用 setAnimator ( int transitionType, Animator animator ) 設置自定義的動畫
- 為所需要的 ViewGroup 通過 setLayoutTransition( LayoutTransition ) 添加一個 LayoutTransition
需要注意的是在setAnimator()中的第一個參數 transitionType 決定了動畫的類型,一共有四種:
APPEARING:元素在容器中顯現時需要動畫顯示
CHANGE_APPEARING:由于容器中要顯現一個新的元素,其它元素的變化需要動畫顯示
DISAPPEARING:元素在容器中消失時需要動畫顯示
-
CHANGE_DISAPPEARING:由于容器中某個元素要消失,其它元素的變化需要動畫顯示
代碼: LayoutTransition layoutTransition = new LayoutTransition(); //設置這個動畫為縮放 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(null,"scaleX",0f,1f); layoutTransition.setAnimator(LayoutTransition.APPEARING,objectAnimator); gridContainer.setLayoutTransition(layoutTransition); //通過點擊添加按鈕,往gridContainer添加button,這里就涉及到了上面第一種類型的動畫:appear add_button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Button newButton = new Button(MainActivity.this); newButton.setText(String.valueOf(num++)); gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount())); } });
可以看到,按鈕出現的時候是以縮放的形式出現的
更多關于 LayoutTransition 請查看文檔
https://developer.android.com/reference/android/animation/LayoutTransition.html
Using a TypeEvaluator
在第一篇的時候我們就有講到 Evaluator 這個類。請復習 Android 動畫之屬性動畫(一)>Evaluator 類。有些時候你需要返回的動畫返回的 value 不是系統默認的 Int,Float 或 Color 類型的,那么你就可以自己自定義這個 Evaluator 來返回你所需要的類型!比如說一個坐標點。這也是 屬性動畫 比 補間動畫 強大的精髓所在。
那要如何自己實現呢,首先需要繼承 TypeEvaluator 接口,然后重寫里面的 evaluate() 方法。
//其實FloatEvaluator也是繼承TypeEvaluator,來實現的
public class FloatEvaluator implements TypeEvaluator {
//注意看到fraction參數,在第一篇里面提到的參數
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
下面我們來實現一個返回一個坐標點的 Evaluator
首先新建一個點類:Point
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(){
return y;
}
}
然后我們新建一個 PointEvaluator ,在里面要實現的是當我們傳入一個初始點和結束點,然后實現平緩從初始點到結束點
public class PointEvaluator implements TypeEvaluator {
float x;
float y;
@Override
public Object evaluate(float fraction, Object s, Object e) {
Point startPoint = (Point) s;
Point endPoint = (Point) e;
float x = startPoint.getX()+fraction*(endPoint.getX()-startPoint.getX());
float y = startPoint.getY()+fraction*(endPoint.getY()-startPoint.getY());
Point point = new Point(x,y);
return point;
}
}
然后我們新建一個view,在view里面有一個繪制一個圓,通過對圓心坐標的動畫實現球的動畫
代碼的思路就是在左上角畫一個圓,然后對圓心坐標進行進行動畫,為動畫監聽,每得到一個新的圓心坐標就重新繪制view,下面代碼略長,耐心看是很容易看懂的
public class myView extends View {
Paint mpaint;
Point CurrentPoint = null;
float radius;
public myView(Context context) {
this(context, null);
}
public myView(Context context, AttributeSet attrs) {
super(context, attrs);
mpaint = new Paint();
mpaint.setStyle(Paint.Style.FILL);
mpaint.setColor(Color.BLUE);
radius = 25f;
}
//CurrentPoint用來記錄當前圓心的位置,用來告訴canvas在哪繪制圓,Currentpoint為空時
則新建一個坐標點,然后設置動畫
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (CurrentPoint == null) {
CurrentPoint = new Point(radius, radius);
drawCircle(canvas);
setAnimation();
} else {
drawCircle(canvas);
}
}
//根據當前坐標畫圓
private void drawCircle(Canvas canvas) {
canvas.drawCircle(CurrentPoint.getX(), CurrentPoint.getY(), radius, mpaint);
}
//設置動畫。每當有新的point返,則通知view重繪
public void setAnimation() {
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),
new Point(radius,radius),
new Point(getWidth() - radius, getHeight() - radius));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
CurrentPoint = (Point) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(2000);
valueAnimator.start();
}
}
到此我們已經完成了自定義的 Evaluator ,請自行運行查看效果
Using Interpolators##
前面已經提到過 Interpolators 的作用,這里不再重復。如果你沒有找到適合自己的 Interpolators ,那么就可以繼承 Interpolators 然后重寫 getInterpolation()
方法
以下是系統實現 AccelerateDecelerateInterpolator 和 LinearInterpolator 的算法:
AccelerateDecelerateInterpolator
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
LinearInterpolator
public float getInterpolation(float input) {
return input;
}