Android屬性動畫,從某種角度來看,屬性動畫是增強版的補間動畫,屬性動畫的強大可以展現在如下兩方面.
> 補間動畫隻能定義兩個關鍵幀在"透明度","旋轉","傾斜","位移"4個方面的變化,但屬性動畫可以定義任何屬性的變化.
> 補間動畫隻能對UI元件執行動畫,但屬性動畫幾乎可以對任何對象執行動畫(不管它是否顯示在螢幕上).
與補間動畫類似的是,屬性動畫也需要定義如下幾個方面的屬性.
> 動畫持續時間.該屬性的預設值是300毫秒.在屬性動畫資源檔案中通過android:duration屬性指定.
> 動畫插值方式.該屬性的作用與補間動畫中插值屬性的作用基本類似.在屬性動畫資源檔案中通過android:interpolator屬性指定.
> 動畫重複次數.指定動畫重複播放的次數.在屬性動畫資源檔案中通過android:repeatCount屬性指定.
> 重複行為. 指定動畫播放結束後,重複下次動畫時,是從開始幀再次播放到結束幀,還是從結束幀反向播放到開始幀.在屬性動畫資源檔案中通過android:repeatMode屬性指定.
> 動畫集.開發者可以将多個屬性動畫合并成一組,即可讓這組屬性動畫按次序播放,也可讓這組屬性動畫同時播放.在屬性動畫資源檔案中通過<set.../>元素來組合,該元素的android:ordering屬性指定改組動畫的按次序播放,還是同時播放.
> 幀重新整理頻率. 指定每隔多長時間播放一幀.該屬性預設值為10毫秒.
屬性動畫的API
屬性動畫共涉及如下API.
> Animator: 它提供了建立屬性動畫的基類,基本上不會直接的使用該類.通常該類隻用于被繼承并重寫它的相關方法.
> ValueAnimator: 屬性動畫主要的事件引擎,它負責計算各個幀的屬性值.它定義了屬性動畫的絕大部分核心功能,包括計算各幀的相關屬性值,負責處理更新事件,按屬性值的類型控制計算規則.屬性動畫主要由兩方面組成:(1)計算各幀的相關屬性值;(2)為指定對象設定這些計算後的值.ValueAnimator隻負責第一方面内容,是以程式員必須根據ValueAnimator計算并監聽更新來更新對象的相關屬性值.
> ObjectAnimator: 它是ValueAnimator子類,允許程式員對指定對象的屬性執行動畫.實際應用中.ObjectAnimator使用起來更加簡單,是以更加常用.在少數場景下,由于ObjectAnimator存在一些限制,可能需要考慮使用ValueAnimator.
> AnimatorSet: 它是Animator的子類,用于組合多個Animator,并指定多個Animator是按次序播放,還是同時播放.
除此之外,屬性動畫還需要利用一個Evaluator(電腦),該工具類控制屬性動畫如何計算屬性值.Android提供了如下Evaluator.
> IntEvaluator: 用于計算int類型屬性值的電腦.
> FloatEvaluator: 用于計算float類型屬性值的電腦.
> ArgbEvaluator: 用于計算以十六進制形式表示的顔色值的電腦.
> TypeEvaluator: 它是電腦接口,開發者可以通過實作該接口來實作自定義電腦.
如果需要對int,float或者顔色值以外類型的屬性執行屬性動畫,可能需要實作TypeEvaluator接口來實作定義電腦.
1.使用ValueAnimator建立動畫
使用ValueAnimator動畫可按如下4個步驟:
(1)調用ValueAnimator的ofInt(),ofFloat()或ofObject()靜态方法建立ValueAnimator執行個體.
(2)調用ValueAnimator的setXxx()設定動畫持續時間,插值方式,重複次數等.
(3)調用ValueAnimator的start()方法啟動動畫.
(4)為ValueAnimator注冊AnimatorUpdateListener監聽器,在該監聽器中可以監聽ValueAnimator計算出來的值的改變,并将這些值應用到指定對象.
例如如下代碼片段:
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(1000);
animator.start();
上面的例子實作了在1000ms内,值從0~1的變化.
除此之外,開發者也可以提供一個自定義的Evaluator電腦,例如如下代碼:
ValueAnimator animator = ValueAnimator.ofObject(new MyTypeEvaluator(), startVal, endVal);
animator.setDuration(1000);
animator.start();
上面的代碼片段中,ValueAnimator僅僅是計算動畫過程中變化的值,并沒有把這些計算出來的值應用到任何對象上,是以也不會顯示任何動畫.
如果希望使用ValueAnimator建立動畫,還需要注冊一個監聽器:AnimatorUpdateListener,該監聽器負責更新對象的屬性值.在實作這個監聽器的時候,可以通過getAnimatedValue()的方法來擷取目前幀的值,并将該計算出來的值應用到指定對象上.當該對象的屬性持續改變時,該對象也就呈現出動畫效果.
2.使用ObjectAnimator建立動畫
ObjectAnimator繼承了ValueAnimator,是以它可以直接将ValueAnimator在動畫過程中計算出來的值應用到指定對象的指定屬性上(ValueAnimator則需要注冊一個監聽器來完成這個工作).是以使用ObjectAnimator就不需要注冊AnimatorUpdateListener監聽器.
使用ObjectAnimator的ofInt(),ofFloat()或ofObject()靜态方法建立ObjectAnimator時,需要指定具體的對象,以及對象的屬性名.
例如如下代碼片段:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
與ValueAnimator不同的是,使用ObjectAnimator有如下幾個注意點:
> 要為該對象對應的屬性提供setter方法,如上例中需要為foo對象提供setAlpha(float value)方法.
> 如果調用ObjectAnimator的ofInt(),ofFloat或ofObject工廠方法時values...參數隻提供了一個值(本來需要提供開始值和結束值),那麼該值會被認為是結束值.
那麼該對象應該為該屬性提供一個getter方法,該getter方法的傳回值将被作為開始值.
> 如果動畫的對象是View,為了能顯示動畫效果,可能還需要在onAnimationUpdate()事件監聽方法中調用View.invalidate()方法來重新整理螢幕的顯示,比如對Drawable對象的color屬性執行動畫.但View定義的setter方法,如setAlpha()和setTranslationX()等方法,都會主動地調用invalidate()方法,是以不需要額外地調用invalidate()方法.
使用屬性動畫
屬性動畫即可作為于UI元件,也可作用于普通的對象(即使它沒有在UI界面上繪制出來).
定義屬性動畫有如下兩種方式:
> 使用ValueAnimator或者ObjectAnimator的靜态工廠方法來建立動畫.
> 使用資源檔案來定義動畫.
使用屬性動畫的步驟如下:
(1)建立ValueAnimator或ObjectAnimator對象------即可從XML資源檔案加載該動畫資源,也可直接調用ValueAnimator或ObjectAnimator的靜态工廠方法來建立動畫.
(2) 根據需要為Animator對象設定屬性.
(3) 如果需要監聽Animator的動畫開始事件,動畫結束事件,動畫重複事件,動畫值改變事件,并根據事件提供響應處理代碼,應該為Animator對象設定事件監聽器.
(4) 如果有多個動畫需要按次序或同時播放,應使用AnimatorSet組合這些動畫.
(5)調用Animator對象的start()方法啟動動畫.
下面的示例示範了如何利用屬性動畫來控制"小球"掉落動畫,該示例會監聽使用者在螢幕上的"觸屏"時間,程式會在螢幕的觸摸點繪制一個小球,并用動畫控制小球向下掉落.
1.主要代碼:
package com.example.animatordemo;
import java.util.ArrayList;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
/** 定義小球的大小的常量 */
private static final float BALL_SIZE = 50F;
/** 定義小球從螢幕上放下落到螢幕底端的時間 */
private static final float FULL_TIME = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout container = (LinearLayout) findViewById(R.id.linear_layout);
// 設定該視窗顯示MyAnimationView元件
container.addView(new MyAnimationView(this));
}
public class MyAnimationView extends View implements AnimatorUpdateListener {
/** 圓球的資料集 */
public final ArrayList<ShapeHolder> balls = new ArrayList<ShapeHolder>();
public MyAnimationView(Context context) {
this(context, null);
}
public MyAnimationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyAnimationView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.WHITE);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果觸碰事件不是按下,移動事件
if (event.getAction() != MotionEvent.ACTION_DOWN
&& event.getAction() != MotionEvent.ACTION_MOVE) {
return false;
}
// 在事件發生點添加一個小球(用一個圓形代替)
ShapeHolder newBall = addBall(event.getX(), event.getY());
// 計算小球下落動畫開始時的y坐标
float startY = newBall.getY();
// 計算小球下落動畫結束時的y坐标(落到螢幕最下方,就是螢幕高度減去小球高度)
float endY = getHeight() - BALL_SIZE;
// 擷取螢幕高度
float height = getHeight();
// 觸摸點的y坐标
float eventY = event.getY();
// 計算動畫的持續時間
int duration = (int) (FULL_TIME * ((height - eventY) / height));
// 定義小球"落下"的動畫:讓newBall對象的y屬性從事件發生點變化到螢幕最下方
ValueAnimator fallAnim = ObjectAnimator.ofFloat(newBall, "y",
startY, endY);
// 設定fallAnim動畫的持續時間
fallAnim.setDuration(duration);
// 設定fallAnim動畫的插值方式:加速插值
fallAnim.setInterpolator(new AccelerateInterpolator());
// 為fallAnim動畫添加監聽器
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
fallAnim.addUpdateListener(this);
// 定義對newBall對象的alpha屬性執行從1到0的動畫(即定義漸隐動畫)
ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha",
1f, 0f);
// 設定動畫持續時間
fadeAnim.setDuration(250);
// 為fadeAnim動畫添加監聽器
fadeAnim.addListener(new AnimatorListener() {
// 動畫結束時
@Override
public void onAnimationEnd(Animator animation) {
// 動畫結束時,将該動畫關聯的ShapeHolder删除
balls.remove(((ObjectAnimator) animation).getTarget());
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
// 為fadeAnim動畫添加監聽器
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
fadeAnim.addUpdateListener(this);
// 定義一個AnimatorSet來組合動畫
AnimatorSet animatorSet = new AnimatorSet();
// 指定在播放fadeAnim動畫之前,先播放bouncer動畫
animatorSet.play(fallAnim).before(fadeAnim);
// 開始播放動畫
animatorSet.start();
return true;
}
private ShapeHolder addBall(float x, float y) {
// 建立一個圓
OvalShape ovalShape = new OvalShape();
// 設定該橢圓的寬高
ovalShape.resize(BALL_SIZE, BALL_SIZE);
// 将圓包裝成Drawable對象
ShapeDrawable drawable = new ShapeDrawable(ovalShape);
// 建立一個ShapeHolder對象
ShapeHolder holder = new ShapeHolder(drawable);
// 設定ShapeHolder的x,y坐标
holder.setX(x);
holder.setY(y);
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
// 将red, green, blue三個随機數合成ARGB顔色
int colors = 0xff000000 + red << 16 | green << 8 | blue;
// 擷取drawable上關聯的畫筆
Paint paint = drawable.getPaint();
// 将red, green, blue三個随機數除以4得到商值組合成ARGB顔色
int darkColors = 0xff000000 | red / 4 << 16 | green / 4 << 8 | blue
/ 4;
// 建立圓形漸變
RadialGradient gradient = new RadialGradient(37.5f, 12.5f,
BALL_SIZE, colors, darkColors, Shader.TileMode.CLAMP);
paint.setShader(gradient);
// 為ShapeHolder設定Paint畫筆
holder.setPaint(paint);
balls.add(holder);
return holder;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 周遊balls集合中的每個ShapeHolder元素
for (ShapeHolder holder : balls) {
// 儲存canvas目前坐标系統
canvas.save();
// 坐标變換:将畫布坐标系統平移到shapeHolder的X,Y坐标處
canvas.translate(holder.getX(), holder.getY());
// 将holder持有的圓形繪制到canvas上
holder.getShapeDrawable().draw(canvas);
// 恢複Canvas坐标系統
canvas.restore();
}
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 指定重繪該界面
this.invalidate();
}
}
/***
* 小球實體類
*
* @author Administrator
*
*/
public class ShapeHolder {
// X坐标
private float x = 0;
// Y坐标
private float y = 0;
// // 元件寬度
// private float width = 0;
// // 元件高度
// private float height = 0;
private ShapeDrawable shapeDrawable;
// 色值
private int color;
private RadialGradient radialGradient;
// 透明度
private float alpha = 1f;
// 畫筆
private Paint paint;
public ShapeHolder(ShapeDrawable shapeDrawable) {
this.shapeDrawable = shapeDrawable;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
//
// public float getWidth() {
// return width;
// }
//
// public void setWidth(float width) {
// this.width = width;
// }
//
// public float getHeight() {
// return height;
// }
//
// public void setHeight(float height) {
// this.height = height;
// }
public ShapeDrawable getShapeDrawable() {
return shapeDrawable;
}
public void setShapeDrawable(ShapeDrawable shapeDrawable) {
this.shapeDrawable = shapeDrawable;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public RadialGradient getRadialGradient() {
return radialGradient;
}
public void setRadialGradient(RadialGradient radialGradient) {
this.radialGradient = radialGradient;
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
}
public Paint getPaint() {
return paint;
}
public void setPaint(Paint paint) {
this.paint = paint;
}
}
}
這裡說明一下計算顔色值的代碼:
int colors = 0xff000000 + red << 16 | green << 8 | blue;
解釋如下:
> 0xff000000: 代表了透明度為ff,也就是完全不透明.
> red: 代表了一個0~255(00~ff)的随機整數,但這個整數要添加0xff000000中加粗的兩個"位"上,也就是要将red的值左移(16位,對應為十六進制數的4位),這就是red<<16的原因.
> green: 代表了一個0~255(00~ff)的随機整數,但這個整數要添加0xff000000中加粗的兩個"位"上,也就是要将green的值左移(8位,對應為十六進制數的2位),這就是green<<8的原因.
> blue:代表了一個0~255(00~ff)的随機整數,但這個整數要添加0xff000000中加粗的兩個"位"上,是以blue值就不需要位移了.
将red, green, blue 位移後的結果加起來就得到了實際的顔色值,但為了更好地計算性能,本代碼直接使用按位或(|)來累加這些值.
下面的例子是對上一個例子的改進,主要是為小球增加了幾個動畫,控制小球鑼刀底端時小球被壓扁,小球會再次彈起,這樣就可以開發出"大珠小珠落玉盤"的彈起動畫.
package com.example.animatordemo;
import java.util.ArrayList;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
/** 定義小球的大小的常量 */
private static final float BALL_SIZE = 50F;
/** 定義小球從螢幕上放下落到螢幕底端的時間 */
private static final float FULL_TIME = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout container = (LinearLayout) findViewById(R.id.linear_layout);
// 設定該視窗顯示MyAnimationView元件
container.addView(new MyAnimationView(this));
}
public class MyAnimationView extends View implements AnimatorUpdateListener {
/** 圓球的資料集 */
public final ArrayList<ShapeHolder> balls = new ArrayList<ShapeHolder>();
public MyAnimationView(Context context) {
this(context, null);
}
public MyAnimationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyAnimationView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.WHITE);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果觸碰事件不是按下,移動事件
if (event.getAction() != MotionEvent.ACTION_DOWN
&& event.getAction() != MotionEvent.ACTION_MOVE) {
return false;
}
// 在事件發生點添加一個小球(用一個圓形代替)
ShapeHolder newBall = addBall(event.getX(), event.getY());
// 計算小球下落動畫開始時的y坐标
float startY = newBall.getY();
// 計算小球下落動畫結束時的y坐标(落到螢幕最下方,就是螢幕高度減去小球高度)
float endY = getHeight() - BALL_SIZE;
// 擷取螢幕高度
float height = getHeight();
// 觸摸點的y坐标
float eventY = event.getY();
// 計算動畫的持續時間
int duration = (int) (FULL_TIME * ((height - eventY) / height));
// 定義小球"落下"的動畫:讓newBall對象的y屬性從事件發生點變化到螢幕最下方
ValueAnimator fallAnim = ObjectAnimator.ofFloat(newBall, "y",
startY, endY);
// 設定fallAnim動畫的持續時間
fallAnim.setDuration(duration);
// 設定fallAnim動畫的插值方式:加速插值
fallAnim.setInterpolator(new AccelerateInterpolator());
// 為fallAnim動畫添加監聽器
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
fallAnim.addUpdateListener(this);
//-------------------
//定義小球"壓扁"的動畫:該動畫控制小球的X坐标"向左移"半個球
ValueAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall, "x", newBall.getX(), newBall.getX() - BALL_SIZE / 2);
//設定squashAnim1動畫的持續時間
squashAnim1.setDuration(duration / 4);
//設定squashAnim1動畫的重複次數
squashAnim1.setRepeatCount(1);
//設定squashAnim1動畫的重複方式
squashAnim1.setRepeatMode(ValueAnimator.REVERSE);
//設定squashAnim1動畫的插值方式:減速插值
squashAnim1.setInterpolator(new DecelerateInterpolator());
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
squashAnim1.addUpdateListener(this);
//定義小球"壓扁"的動畫:該動畫控制小球的寬度加倍
ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall, "width", newBall.getWidth(), newBall.getWidth() + BALL_SIZE);
//設定squashAnim2動畫的持續時間
squashAnim2.setDuration(duration / 4);
//設定squashAnim2動畫的重複次數
squashAnim2.setRepeatCount(1);
//設定squashAnim2動畫的重複方式
squashAnim2.setRepeatMode(ValueAnimator.REVERSE);
//設定squashAnim2動畫的插值方式:減速插值
squashAnim2.setInterpolator(new DecelerateInterpolator());
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
squashAnim2.addUpdateListener(this);
//定義小球"拉伸"的動畫:該動畫控制小球的Y坐标"向下移"半個球
ObjectAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall, "y", endY, endY + BALL_SIZE / 2);
//設定stretchAnim1動畫的持續時間
stretchAnim1.setDuration(duration / 4);
//設定stretchAnim1動畫的重複次數
stretchAnim1.setRepeatCount(1);
//設定stretchAnim1動畫的重複方式
stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);
//設定stretchAnim1動畫的插值方式:減速插值
stretchAnim1.setInterpolator(new DecelerateInterpolator());
// 當ObjectAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
stretchAnim1.addUpdateListener(this);
//定義小球"拉伸"的動畫:該動畫控制小球的高度減半
ValueAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall, "height", newBall.getHeight(), newBall.getHeight() - BALL_SIZE / 2);
//設定stretchAnim2動畫的持續時間
stretchAnim2.setDuration(duration / 4);
//設定stretchAnim2動畫的重複次數
stretchAnim2.setRepeatCount(1);
//設定stretchAnim2動畫的重複方式
stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);
//設定stretchAnim2動畫的插值方式:減速插值
stretchAnim2.setInterpolator(new DecelerateInterpolator());
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
stretchAnim2.addUpdateListener(this);
//定義小球"彈起"的動畫
ObjectAnimator bounceBackAnim = ObjectAnimator.ofFloat(newBall, "y", endY, startY);
//設定bounceBackAnim動畫持續時間
bounceBackAnim.setDuration(duration);
//設定bounceBackAnim動畫的插值方式:減速插值
bounceBackAnim.setInterpolator(new DecelerateInterpolator());
// 當ObjectAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
bounceBackAnim.addUpdateListener(this);
//使用AnimatorSet按順序播放"掉落/壓扁&拉伸/彈起動畫"
AnimatorSet bouncer = new AnimatorSet();
//定義在squashAnim1動畫之前播放fallAnim下落動畫
bouncer.play(fallAnim).before(squashAnim1);
//由于小球在"螢幕"下方彈起時,小球要被壓扁
//即:寬度加倍.X坐标左移半個球的距離;高度減半,Y坐标下移半個球的距離
//是以此處指定播放squashAnim1動畫的同時
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
//還播放stretchAnim2動畫之後,播放bounceBackAnim彈起動畫
bouncer.play(bounceBackAnim).after(stretchAnim2);
//-----------------------------
// 定義對newBall對象的alpha屬性執行從1到0的動畫(即定義漸隐動畫)
ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha",
1f, 0f);
// 設定動畫持續時間
fadeAnim.setDuration(250);
// 為fadeAnim動畫添加監聽器
fadeAnim.addListener(new AnimatorListener() {
// 動畫結束時
@Override
public void onAnimationEnd(Animator animation) {
// 動畫結束時,将該動畫關聯的ShapeHolder删除
balls.remove(((ObjectAnimator) animation).getTarget());
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
// 為fadeAnim動畫添加監聽器
// 當ValueAnimator的屬性值發生改變時,将會激發該監聽器的事件監聽方法
fadeAnim.addUpdateListener(this);
// 再次定義一個AnimatorSet來組合動畫
AnimatorSet animatorSet = new AnimatorSet();
// 指定在播放fadeAnim動畫之前,先播放bouncer動畫
animatorSet.play(bouncer).before(fadeAnim);
// 開始播放動畫
animatorSet.start();
return true;
}
private ShapeHolder addBall(float x, float y) {
// 建立一個圓
OvalShape ovalShape = new OvalShape();
// 設定該橢圓的寬高
ovalShape.resize(BALL_SIZE, BALL_SIZE);
// 将圓包裝成Drawable對象
ShapeDrawable drawable = new ShapeDrawable(ovalShape);
// 建立一個ShapeHolder對象
ShapeHolder holder = new ShapeHolder(drawable);
// 設定ShapeHolder的x,y坐标
holder.setX(x);
holder.setY(y);
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
// 将red, green, blue三個随機數合成ARGB顔色
int colors = 0xff000000 + red << 16 | green << 8 | blue;
// 擷取drawable上關聯的畫筆
Paint paint = drawable.getPaint();
// 将red, green, blue三個随機數除以4得到商值組合成ARGB顔色
int darkColors = 0xff000000 | red / 4 << 16 | green / 4 << 8 | blue
/ 4;
// 建立圓形漸變
RadialGradient gradient = new RadialGradient(37.5f, 12.5f,
BALL_SIZE, colors, darkColors, Shader.TileMode.CLAMP);
paint.setShader(gradient);
// 為ShapeHolder設定Paint畫筆
holder.setPaint(paint);
balls.add(holder);
return holder;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 周遊balls集合中的每個ShapeHolder元素
for (ShapeHolder holder : balls) {
// 儲存canvas目前坐标系統
canvas.save();
// 坐标變換:将畫布坐标系統平移到shapeHolder的X,Y坐标處
canvas.translate(holder.getX(), holder.getY());
// 将holder持有的圓形繪制到canvas上
holder.getShapeDrawable().draw(canvas);
// 恢複Canvas坐标系統
canvas.restore();
}
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 指定重繪該界面
this.invalidate();
}
}
/***
* 小球實體類
*
* @author Administrator
*
*/
public class ShapeHolder {
// X坐标
private float x = 0;
// Y坐标
private float y = 0;
// 元件寬度
private float width = 0;
// 元件高度
private float height = 0;
private ShapeDrawable shapeDrawable;
// 色值
private int color;
private RadialGradient radialGradient;
// 透明度
private float alpha = 1f;
// 畫筆
private Paint paint;
public ShapeHolder(ShapeDrawable shapeDrawable) {
this.shapeDrawable = shapeDrawable;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public ShapeDrawable getShapeDrawable() {
return shapeDrawable;
}
public void setShapeDrawable(ShapeDrawable shapeDrawable) {
this.shapeDrawable = shapeDrawable;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public RadialGradient getRadialGradient() {
return radialGradient;
}
public void setRadialGradient(RadialGradient radialGradient) {
this.radialGradient = radialGradient;
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
}
public Paint getPaint() {
return paint;
}
public void setPaint(Paint paint) {
this.paint = paint;
}
}
}