天天看點

Android 動畫之屬性動畫(Animator)

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;
		}
	}

}