Android中動畫的詳細講解
阿新 • • 發佈:2019-02-08
逐幀動畫:
補間動畫
結合動畫
自定義動畫
屬性動畫一
屬性動畫二
動畫三
動畫四
逐幀動畫
定義逐幀動畫,只要在<animation-list……>;元素中使用<item…/>子元素定義動畫的全部幀就可以了!
語法格式:
<?xml version="1.0" encoding="utf-8"?>
<!-- 指定動畫迴圈播放 -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot ="false">
<!-- 新增多個幀 -->
<item android:drawable="圖片" android:duration="60" />
</animation-list >
MainActivity程式碼:
public class MainActivity extends Activity
{
private MyView myView;
private AnimationDrawable anim;
private MediaPlayer bomb;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 使用FrameLayout佈局管理器,它允許元件自己控制位置
FrameLayout frame = new FrameLayout(this);
setContentView(frame);
// 設定使用背景
frame.setBackgroundResource(R.drawable.back);
// 載入音效
bomb = MediaPlayer.create(this, R.raw.bomb);
myView = new MyView(this);
// 設定myView用於顯示blast動畫
myView.setBackgroundResource(R.anim.blast);
// 設定myView預設為隱藏
myView.setVisibility(View.INVISIBLE);
// 獲取動畫物件
anim = (AnimationDrawable) myView.getBackground();
frame.addView(myView);
frame.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View source, MotionEvent event)
{
// 只處理按下事件(避免每次產生兩個動畫效果)
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
// 先停止動畫播放
anim.stop();
float x = event.getX();
float y = event.getY();
// 控制myView的顯示位置
myView.setLocation((int) y - 40, (int) x - 20);
myView.setVisibility(View.VISIBLE);
// 啟動動畫
anim.start();
// 播放音效
bomb.start();
}
return false;
}
});
}
// 定義一個自定義View,該自定義View用於播放“爆炸”效果
class MyView extends ImageView
{
public MyView(Context context)
{
super(context);
}
// 定義一個方法,該方法用於控制MyView的顯示位置
public void setLocation(int top, int left)
{
this.setFrame(left-100, top-100, left + 100, top + 100);
}
// 重寫該方法,控制如果動畫播放到最後一幀時,隱藏該View
@Override
protected void onDraw(Canvas canvas) // ①
{
try
{
Field field = AnimationDrawable.class
.getDeclaredField("mCurFrame");
field.setAccessible(true);
// 獲取anim動畫的當前幀
int curFrame = field.getInt(anim);
// 如果已經到了最後一幀
if (curFrame == anim.getNumberOfFrames() - 1)
{
// 讓該View隱藏
setVisibility(View.INVISIBLE);
}
}
catch (Exception e)
{
}
super.onDraw(canvas);
}
}
}
補間動畫(Tween)
資源定義完成後,可以使用AnimationUtils工具類載入指定的動畫資源。
Animation子類:
- AlphaAnimation:
- ScaleAnimation:
- TranslateAnimation:
- RotateAnimation:
interpolator簡單理解
interpolator:根據特定的演算法計算出整個動畫所需要動態插入幀的密度和位置。負責控制動畫的變化速度。使其動畫效果更流暢。
程式碼
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView flower = (ImageView)
findViewById(R.id.flower);
// 載入第一份動畫資源
final Animation anim = AnimationUtils
.loadAnimation(this, R.anim.anim);
// 設定動畫結束後保留結束狀態
anim.setFillAfter(true);
// 載入第二份動畫資源
final Animation reverse = AnimationUtils.loadAnimation(this
, R.anim.reverse);
// 設定動畫結束後保留結束狀態
reverse.setFillAfter(true);
Button bn = (Button) findViewById(R.id.bn);
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
flower.startAnimation(reverse);
}
}
};
bn.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
flower.startAnimation(anim);
// 設定3.5秒後啟動第二個動畫
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
}, 3500);
}
});
}
}
結合動畫
MainActivity程式碼:
public class MainActivity extends Activity
{
// 記錄蝴蝶ImageView當前的位置
private float curX = 0;
private float curY = 30;
// 記錄蝴蝶ImageView下一個位置的座標
float nextX = 0;
float nextY = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取顯示蝴蝶的ImageView元件
final ImageView imageView = (ImageView)
findViewById(R.id.butterfly);
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
// 橫向上一直向右飛
if (nextX > 320)
{
curX = nextX = 0;
}
else
{
nextX += 8;
}
// 縱向上可以隨機上下
nextY = curY + (float) (Math.random() * 10 - 5);
// 設定顯示蝴蝶的ImageView發生位移改變
TranslateAnimation anim = new TranslateAnimation(
curX, nextX, curY, nextY);
curX = nextX;
curY = nextY;
anim.setDuration(200);
// 開始位移動畫
imageView.startAnimation(anim); // ①
}
}
};
final AnimationDrawable butterfly = (AnimationDrawable)
imageView.getBackground();
imageView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 開始播放蝴蝶振翅的逐幀動畫
butterfly.start(); // ②
// 通過定製器控制每0.2秒執行一
// 次TranslateAnimation動畫
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
}, 0, 200);
}
});
}
}
自定義補間動畫:
自定義補間動畫需要繼承Animation,重寫抽象方法applyTransformation(float interpolatedTime,Transformation t)方法,引數說明:
- interpolatedTime:代表動畫的時間進行比,無論動畫實際的持續時間如何,當動畫播放時,該引數總是從0變化到1
- Transformation:代表補間動畫在不同時刻對圖形或者元件的變形程度
Camera常用方法如下:
getMatrix(Matrix matrix):將Camera所做的變換應用到指定matrix上。
rotateX(float deg):使目標元件沿X軸旋轉。
rotateY(float deg):使目標元件沿Y軸旋轉。
rotateZ(float deg):使目標元件沿Z軸旋轉。
translate(float x,float y,float z):使目標元件在三維空間裡進行位移變換
applyToCanvas(Canvas canvas):把Camera所做的變換應用到Canvas上。
MyAnimation程式碼:
public class MyAnimation extends Animation
{
private float centerX;
private float centerY;
// 定義動畫的持續事件
private int duration;
private Camera camera = new Camera();
public MyAnimation(float x, float y, int duration)
{
this.centerX = x;
this.centerY = y;
this.duration = duration;
}
@Override
public void initialize(int width, int height
, int parentWidth, int parentHeight)
{
super.initialize(width, height, parentWidth, parentHeight);
// 設定動畫的持續時間
setDuration(duration);
// 設定動畫結束後效果保留
setFillAfter(true);
setInterpolator(new LinearInterpolator());
}
/*
* 該方法的interpolatedTime代表了抽象的動畫持續時間,不管動畫實際持續時間多長,
* interpolatedTime引數總是從0(動畫開始時)~1(動畫結束時)
* Transformation引數代表了對目標元件所做的改變.
*/
@Override
protected void applyTransformation(float interpolatedTime
, Transformation t)
{
camera.save();
// 根據interpolatedTime時間來控制X、Y、Z上的偏移
camera.translate(100.0f - 100.0f * interpolatedTime,
150.0f * interpolatedTime - 150,
80.0f - 80.0f * interpolatedTime);
// 設定根據interpolatedTime時間在Y軸上旋轉不同角度
camera.rotateY(360 * (interpolatedTime));
// 設定根據interpolatedTime時間在X軸上旋轉不同角度
camera.rotateX((360 * interpolatedTime));
// 獲取Transformation引數的Matrix物件
Matrix matrix = t.getMatrix();
camera.getMatrix(matrix);
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
camera.restore();
}
}
MainActivity程式碼:
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取ListView元件
ListView list = (ListView) findViewById(R.id.list);
WindowManager windowManager = (WindowManager)
getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrice = new DisplayMetrics();
// 獲取螢幕的寬和高
display.getMetrics(metrice);
// 設定對ListView元件應用動畫
list.setAnimation(new MyAnimation(metrice.xdpi / 2
, metrice.ydpi / 2, 3500));
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/bookArray"
/>
</LinearLayout>
屬性動畫
定義屬性動畫的方法:
- 使用ValurAnimator或者ObjectAnimator的靜態工廠方法來建立動畫
- 使用資原始檔來定義動畫
使用屬性動畫的步驟
- 建立ValurAniamtor或ObjectAnimator物件,即可從XML資源載入,也可以使用ValurAniamtor或ObjectAnimator的靜態工廠方法建立
- 根據需要設定Animator屬性值
- 如果需要監聽Animator的改變事件,需要為Animator設定事件監聽器
- 如果有多個動畫需要按次序或者同時處理,則需要使用AnimatorSet組合這些動畫
呼叫Animator的start()方法來啟動。
屬性動畫和補間動畫的區別:
補間動畫只能定義兩個關鍵幀在“透明度
”“旋轉”“縮放”“位移”4個方面變化。屬性動畫可以定義任何屬性的變化- 補間動畫只對UI元件執行動畫,但屬性動畫幾乎可以對任何物件執行動畫,不管是否顯示在螢幕上
屬性動畫的API:
- Animator:它提供建立屬性動畫的基類,通常用於被繼承並重寫它的相關方法
- ValueAnimator:用於計算相關屬性值
- ObjectAnimator:ValueAnimator的子類,允許程式設計師對指定物件的屬性執行動畫。
- AnimatorSet:Animator的子類,用於組合多個Animator,並指定多個Animator按次序播放還是同時播放。
- IntEvaluator:用於計算int型別的計算器
- FloatEvaluator:用於計算float型別屬性值的計算器
- ArgbEvaluator:用於計算以十六進位制形式表示的顏色值的計算器
- TypeEvaluator:計算器介面,開發者可以通過實現該介面來實現自定義計算器。
屬性動畫一
ShapeHolder程式碼:
public class ShapeHolder
{
private float x = 0, y = 0;
private ShapeDrawable shape;
private int color;
private RadialGradient gradient;
private float alpha = 1f;
private Paint paint;
public ShapeHolder(ShapeDrawable s)
{
shape = s;
}
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 ShapeDrawable getShape()
{
return shape;
}
public void setShape(ShapeDrawable shape)
{
this.shape = shape;
}
public int getColor()
{
return color;
}
public void setColor(int color)
{
this.color = color;
}
public RadialGradient getGradient()
{
return gradient;
}
public void setGradient(RadialGradient gradient)
{
this.gradient = gradient;
}
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;
}
}
MainActivity程式碼;
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取ListView元件
ListView list = (ListView) findViewById(R.id.list);
WindowManager windowManager = (WindowManager)
getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrice = new DisplayMetrics();
// 獲取螢幕的寬和高
display.getMetrics(metrice);
// 設定對ListView元件應用動畫
list.setAnimation(new MyAnimation(metrice.xdpi / 2
, metrice.ydpi / 2, 3500));
}
}
屬性動畫二:
ShapeHolder程式碼:
同屬性動畫一中的ShapeHolder程式碼:
MainActivity程式碼:
public class MainActivity extends Activity
{
// 定義小球的大小的常量
static final float BALL_SIZE = 50F;
// 定義小球從螢幕上方下落到螢幕底端的總時間
static final float FULL_TIME = 3000;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout container = (LinearLayout)
findViewById(R.id.container);
// 設定該視窗顯示MyAnimationView元件
container.addView(new MyAnimationView(this));
}
public class MyAnimationView extends View
{
public final ArrayList<ShapeHolder> balls
= new ArrayList<ShapeHolder>();
public MyAnimationView(Context context)
{
super(context);
// 載入動畫資源
ObjectAnimator colorAnim = (ObjectAnimator) AnimatorInflater
.loadAnimator(MainActivity.this, R.animator.color_anim);
colorAnim.setEvaluator(new ArgbEvaluator());
// 對該View本身應用屬性動畫
colorAnim.setTarget(this);
// 開始指定動畫
colorAnim.start();
}
@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 h = (float) getHeight();
float eventY = event.getY();
// 計算動畫的持續時間
int duration = (int) (FULL_TIME * ((h - eventY) / h));
// 定義小球“落下”的動畫:
// 讓newBall物件的y屬性從事件發生點變化到螢幕最下方
ValueAnimator fallAnim = ObjectAnimator.ofFloat(
newBall, "y", startY, endY);
// 設定fallAnim動畫的持續時間
fallAnim.setDuration(duration);
// 設定fallAnim動畫的插值方式:加速插值
fallAnim.setInterpolator(new AccelerateInterpolator());
// 定義小球“壓扁”的動畫:該動畫控制小球的x座標“向左移”半個球
ValueAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall
, "x", newBall.getX(), newBall.getX() - BALL_SIZE / 2);
// 設定squashAnim1動畫持續時間
squashAnim1.setDuration(duration / 4);
// 設定squashAnim1動畫重複1次
squashAnim1.setRepeatCount(1);
// 設定squashAnim1動畫的重複方式
squashAnim1.setRepeatMode(ValueAnimator.REVERSE);
// 設定squashAnim1動畫的插值方式:減速插值
squashAnim1.setInterpolator(new DecelerateInterpolator());
// 定義小球“壓扁”的動畫:該動畫控制小球的寬度加倍
ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall,
"width", newBall.getWidth()
, newBall.getWidth() + BALL_SIZE);
// 設定squashAnim2動畫持續時間
squashAnim2.setDuration(duration / 4);
// 設定squashAnim2動畫重複1次
squashAnim2.setRepeatCount(1);
// 設定squashAnim2動畫的重複方式
squashAnim2.setRepeatMode(ValueAnimator.REVERSE);
// 設定squashAnim2動畫的插值方式:減速插值
squashAnim2.setInterpolator(new DecelerateInterpolator());
// 定義小球“拉伸”的動畫:該動畫控制小球的y座標“向下移”半個球
ObjectAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall
, "y", endY, endY + BALL_SIZE / 2);
// 設定stretchAnim1動畫持續時間
stretchAnim1.setDuration(duration / 4);
// 設定stretchAnim1動畫重複1次
stretchAnim1.setRepeatCount(1);
// 設定stretchAnim1動畫的重複方式
stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);
// 設定stretchAnim1動畫的插值方式:減速插值
stretchAnim1.setInterpolator(new DecelerateInterpolator());
// 定義小球“拉伸”的動畫:該動畫控制小球的高度減半
ValueAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall,
"height", newBall.getHeight()
, newBall.getHeight() - BALL_SIZE / 2);
// 設定stretchAnim2動畫持續時間
stretchAnim2.setDuration(duration / 4);
// 設定squashAnim2動畫重複1次
stretchAnim2.setRepeatCount(1);
// 設定squashAnim2動畫的重複方式
stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);
// 設定squashAnim2動畫的插值方式:減速插值
stretchAnim2.setInterpolator(new DecelerateInterpolator());
// 定義小球“彈起”的動畫
ObjectAnimator bounceBackAnim = ObjectAnimator.ofFloat(
newBall , "y", endY, startY);
// 設定持續時間
bounceBackAnim.setDuration(duration);
// 設定動畫的插值方式:減速插值
bounceBackAnim.setInterpolator(new DecelerateInterpolator());
// 使用AnimatorSet按順序播放“掉落/壓扁&拉伸/彈起動畫
AnimatorSet bouncer = new AnimatorSet();
// 定義在squashAnim1動畫之前播放fallAnim下落動畫
bouncer.play(fallAnim).before(squashAnim1);
// 由於小球在“螢幕”下方彈起時,小球要被壓扁
// 即:寬度加倍、x座標左移半個球,高度減半、y座標下移半個球
// 因此此處指定播放squashAnim1的同時
// 還播放squashAnim2、stretchAnim1、stretchAnim2
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 AnimatorListenerAdapter()
{
// 當動畫結束時
@Override
public void onAnimationEnd(Animator animation)
{
// 動畫結束時將該動畫關聯的ShapeHolder刪除
balls.remove(((ObjectAnimator) animation).getTarget());
}
});
// 再次定義一個AnimatorSet來組合動畫
AnimatorSet animatorSet = new AnimatorSet();
// 指定在播放fadeAnim之前,先播放bouncer動畫
animatorSet.play(bouncer).before(fadeAnim);
// 開發播放動畫
animatorSet.start();
return true;
}
private ShapeHolder addBall(float x, float y)
{
// 建立一個橢圓
OvalShape circle = new OvalShape();
// 設定該橢圓的寬、高
circle.resize(BALL_SIZE, BALL_SIZE);
// 將橢圓包裝成Drawable物件
ShapeDrawable drawable = new ShapeDrawable(circle);
// 建立一個ShapeHolder物件
ShapeHolder shapeHolder = new ShapeHolder(drawable);
// 設定ShapeHolder的x、y座標
shapeHolder.setX(x - BALL_SIZE / 2);
shapeHolder.setY(y - BALL_SIZE / 2);
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
// 將red、green、blue三個隨機數組合成ARGB顏色
int color = 0xff000000 + red << 16 | green << 8 | blue;
// 獲取drawable上關聯的畫筆
Paint paint = drawable.getPaint();
// 將red、green、blue三個隨機數除以4得到商值組合成ARGB顏色
int darkColor = 0xff000000 | red / 4 << 16
| green / 4 << 8 | blue / 4;
// 建立圓形漸變
RadialGradient gradient = new RadialGradient(
37.5f, 12.5f, BALL_SIZE, color, darkColor
, Shader.TileMode.CLAMP);
paint.setShader(gradient);
// 為shapeHolder設定paint畫筆
shapeHolder.setPaint(paint);
balls.add(shapeHolder);
return shapeHolder;
}
@Override
protected void onDraw(Canvas canvas)
{
// 遍歷balls集合中的每個ShapeHolder物件
for (ShapeHolder shapeHolder : balls)
{
// 儲存canvas的當前座標系統
canvas.save();
// 座標變換:將畫布座標系統平移到shapeHolder的X、Y座標處
canvas.translate(shapeHolder.getX()
, shapeHolder.getY());
// 將shapeHolder持有的圓形繪製在Canvas上
shapeHolder.getShape().draw(canvas);
// 恢復Canvas座標系統
canvas.restore();
}
}
}
}
使用SurfaceView實現動畫
使用自定義View繪製圖片的 缺陷
- View缺乏雙緩衝機制
- 當程式需要更新View上的圖片時,程式必須重新繪製View上顯示的整張圖片
- 新執行緒無法直接更新View元件
動畫三
FishView程式碼:
public class FishView extends SurfaceView
implements SurfaceHolder.Callback
{
private SurfaceHolder holder;
private UpdateViewThread updateThread;
private boolean hasSurface;
private Bitmap back;
private Bitmap[] fishs;
private int fishIndex = 0; // 定義變數記錄繪製第幾張魚的圖片
// 下面定義2個變數,記錄魚的初始位置
private float fishX = 778;
private float fishY = 500;
private float fishSpeed = 6; // 魚的遊動速度
// 定義魚遊動的角度
private int fishAngle = new Random().nextInt(60);
Matrix matrix = new Matrix();
public FishView(Context ctx, AttributeSet set)
{
super(ctx, set);
// 獲取該SurfaceView對應的SurfaceHolder,並將該類的例項作為其Callback
holder = getHolder();
holder.addCallback(this);
hasSurface = false;
back = BitmapFactory.decodeResource(ctx.getResources()
, R.drawable.fishbg);
fishs = new Bitmap[10];
// 初始化魚遊動動畫的10張圖片
for(int i = 0 ; i < 10 ; i++)
{
try
{
int fishId = (Integer)R.drawable.class
.getField("fish" + i).get(null);
fishs[i] = BitmapFactory.decodeResource(
ctx.getResources(), fishId);
}
catch(Exception e){
e.printStackTrace();
}
}
}
public void resume()
{
// 建立和啟動影象更新執行緒
if (updateThread == null)
{
updateThread = new UpdateViewThread();
if (hasSurface == true)
updateThread.start();
}
}
public void pause()
{
// 停止影象更新執行緒
if (updateThread != null)
{
updateThread.requestExitAndWait();
updateThread = null;
}
}
// 當SurfaceView被建立時回撥該方法
@Override
public void surfaceCreated(SurfaceHolder holder)
{
hasSurface = true;
resume();
}
// 當SurfaceView將要被銷燬時回撥該方法
public void surfaceDestroyed(SurfaceHolder holder)
{
hasSurface = false;
pause();
}
// 當SurfaceView發生改變時回撥該方法
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
if (updateThread != null)
updateThread.onWindowResize(w, h);
}
class UpdateViewThread extends Thread
{
// 定義一個記錄圖形是否更新完成的旗標
private boolean done;
UpdateViewThread()
{
super();
done = false;
}
@Override
public void run()
{
SurfaceHolder surfaceHolder = holder;
// 重複繪圖迴圈,直到執行緒停止
while (!done)
{
// 鎖定SurfaceView,並返回到要繪圖的Canvas
Canvas canvas = surfaceHolder.lockCanvas(); // ①
// 繪製背景圖片
canvas.drawBitmap(back, 0, 0, null);
// 如果魚“游出”螢幕之外,重新初始魚的位置
if(fishX < 0)
{
fishX = 778;
fishY = 500;
fishAngle = new Random().nextInt(60);
}
if(fishY < 0)
{
fishX = 778;
fishY = 500;
fishAngle = new Random().nextInt(60);
}
// 使用Matrix來控制魚的旋轉角度和位置
matrix.reset();
matrix.setRotate(fishAngle);
matrix.postTranslate(fishX -= fishSpeed * Math
.cos(Math.toRadians(fishAngle))
, fishY -= fishSpeed * Math.sin(Math.toRadians(fishAngle)));
canvas.drawBitmap(fishs[fishIndex++ % fishs.length], matrix, null);
// 解鎖Canvas,並渲染當前影象
surfaceHolder.unlockCanvasAndPost(canvas); // ②
try
{
Thread.sleep(60);
}
catch (InterruptedException e){}
}
}
public void requestExitAndWait()
{
// 把這個執行緒標記為完成,併合併到主程式執行緒
done = true;
try
{
join();
}
catch (InterruptedException ex){}
}
public void onWindowResize(int w, int h){
// 處理SurfaceView的大小改變事件
}
}
}
動畫四
MainActivity程式碼:
public class MainActivity extends Activity
{
private SurfaceHolder holder;
private Paint paint;
final int HEIGHT = 320;
final int WIDTH = 768;
final int X_OFFSET = 5;
private int cx = X_OFFSET;
// 實際的Y軸的位置
int centerY = HEIGHT / 2;
Timer timer = new Timer();
TimerTask task = null;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final SurfaceView surface = (SurfaceView)
findViewById(R.id.show);
// 初始化SurfaceHolder物件
holder = surface.getHolder();
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(3);
Button sin = (Button)findViewById(R.id.sin);
Button cos = (Button)findViewById(R.id.cos);
OnClickListener listener = (new OnClickListener()
{
@Override
public void onClick(final View source)
{
drawBack(holder);
cx = X_OFFSET;
if(task != null)
{
task.cancel();
}
task = new TimerTask()
{
public void run()
{
int cy = source.getId() == R.id.sin ? centerY
- (int)(100 * Math.sin((cx - 5) * 2
* Math.PI / 150))
: centerY - (int)(100 * Math.cos ((cx - 5)
* 2 * Math.PI / 150));
Canvas canvas = holder.lockCanvas(new Rect(cx ,
cy - 2 , cx + 2, cy + 2));
canvas.drawPoint(cx , cy , paint);
cx ++;
if (cx > WIDTH)
{
task.cancel();
task = null;
}
holder.unlockCanvasAndPost(canvas);
}
};
timer.schedule(task , 0 , 30);
}
});
sin.setOnClickListener(listener);
cos.setOnClickListener(listener);
holder.addCallback(new Callback()
{
@Override
public void surfaceChanged(SurfaceHolder holder, int format,