逐帧动画
定义逐帧动画,只要在<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,
int width, int height)
{
drawBack(holder);
}
@Override
public void surfaceCreated(final SurfaceHolder myHolder){ }
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
timer.cancel();
}
});
}
private void drawBack(SurfaceHolder holder)
{
Canvas canvas = holder.lockCanvas();
// 绘制白色背景
canvas.drawColor(Color.WHITE);
Paint p = new Paint();
p.setColor(Color.BLACK);
p.setStrokeWidth(2);
// 绘制坐标轴
canvas.drawLine(X_OFFSET , centerY , WIDTH , centerY , p);
canvas.drawLine(X_OFFSET , 40 , X_OFFSET , HEIGHT , p);
holder.unlockCanvasAndPost(canvas);
holder.lockCanvas(new Rect(0 , 0 , 0 , 0));
holder.unlockCanvasAndPost(canvas);
}
}