《50 Android Hacks 》这本书讲解了Android的50个小技巧,大家有空可以看看,也有不少人翻译。
书中源码作者奇托在:
Macarse/50AH-code · GitHub
https://github.com/Macarse/50AH-code
下面就按照我的理解逐一翻译一下
Hack 7 在canvas上绘制动画
我们要给控件实现动画效果,首先想到的是Android 的动画api,帧动画,补间动画,属性动画等,那我们那我们能不能直接画一个动画效果呢?
利用Canvas的刷新操作,对canvas不理解的可以查查资料,我们可以实现移动的动画,下面来实现一个在屏幕中随意移动的动画:
首先要准备一张画布,把画布放到我们的窗口中,还要准备一张移动的方块,当然方块我们也可以用paint自己画,这就要再套一层
为了实现画布,要扩展view,覆写onDraw(Canvas canvas)方法
小方块是一个独立的视图,这里不把小方块显示在activity中,在它外面又“包”了一层
画布代码:
package com.example.canvasanimation;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
public class DrawView extends View {
private Rectangle mRectangle;
public int width;
public int height;
public DrawView(Context context) {
super(context);
mRectangle = new Rectangle(context, this);
mRectangle.setARGB(255, 255, 0, 0);
mRectangle.setSpeedX(3);
mRectangle.setSpeedY(3);
}
//覆写 onDraw,在画布上操作
@Override
protected void onDraw(Canvas canvas) {
invalidate();
mRectangle.move();
//矩形在同一块画布上画,mRectance外面又包了一层画布,为同一个画布
mRectangle.onDraw(canvas);
}
}
Rectangle就是屏幕中移动的红色方块,也可以放一张图片在屏幕中移动
Rectangle继承View类,重写onDraw()方法,并提供一些setter和getter方法,用于设置小方块的属性。判断碰撞事件的逻辑在moveTo()方法中。
Rectangle实现代码:
package com.example.canvasanimation;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
public class Rectangle extends View {
public static final int MAX_SIZE = 40;
private static final int ALPHA = 255;
private int mCoordX = 0;
private int mCoordY = 0;
private int mRealSize = 40;
private int mSpeedX = 3;
private int mSpeedY = 3;
private boolean goRight = true;
private boolean goDown = true;
private DrawView mDrawView;
private Paint mInnerPaint;
private RectF mDrawRect;
public Rectangle(Context context, DrawView drawView) {
super(context);
mDrawView = drawView;
mInnerPaint = new Paint();
mDrawRect = new RectF();
/* Red is default */
mInnerPaint.setARGB(ALPHA, 255, 0, 0);
mInnerPaint.setAntiAlias(true);
}
public void setARGB(int a, int r, int g, int b) {
mInnerPaint.setARGB(a, r, g, b);
}
public void setX(int newValue) {
mCoordX = newValue;
}
public float getX() {
return mCoordX;
}
public void setY(int newValue) {
mCoordY = newValue;
}
public float getY() {
return mCoordY;
}
public void move() {
moveTo(mSpeedX, mSpeedY);
}
private void moveTo(int goX, int goY) {
// check the borders, and set the direction if a border has reached
if (mCoordX > (mDrawView.width - MAX_SIZE)) {
goRight = false;
}
if (mCoordX < 0) {
goRight = true;
}
if (mCoordY > (mDrawView.height - MAX_SIZE)) {
goDown = false;
}
if (mCoordY < 0) {
goDown = true;
}
// move the x and y
if (goRight) {
mCoordX += goX;
} else {
mCoordX -= goX;
}
if (goDown) {
mCoordY += goY;
} else {
mCoordY -= goY;
}
}
public int getSpeedX() {
return mSpeedX;
}
public void setSpeedX(int speedX) {
mSpeedX = speedX;
}
public int getmSpeedY() {
return mSpeedY;
}
public void setSpeedY(int speedY) {
mSpeedY = speedY;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设定 矩形的大小,参数为左上右下边界
mDrawRect.set(mCoordX, mCoordY, mCoordX + mRealSize, mCoordY + mRealSize);
//画圆角矩形。第一个5为x轴半径,第二个5为y轴半径
canvas.drawRoundRect(mDrawRect, 5, 5, mInnerPaint);
}
public void setSize(int newSize) {
mRealSize = newSize;
}
public int getSize() {
return mRealSize;
}
}
最后把画布放到我们的窗口当中:
package com.example.canvasanimation;
import android.os.Bundle;
import android.app.Activity;
import android.view.Display;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
private DrawView mDrawView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取窗口展示,包含窗口的宽度,长度
Display display = getWindowManager().getDefaultDisplay();
//初始化DrawView
mDrawView = new DrawView(this);
//将窗口的宽高,赋值给mDrawView的宽高,使其充满整个窗口
mDrawView.height = display.getHeight();
mDrawView.width = display.getWidth();
//把窗口设为activity的布局
setContentView(mDrawView);
}
}
当onDraw()方法被调用的时候,我们将会改变小方块的位置然后将它绘制到画布上。这里的invalidate()就是这个Hack的关键,这个函数的作用就是强制一个View进行绘制(即调用onDraw方法)。因此将invalidate()方法放到onDraw()方法里面就意味着每当View绘制完自己的时候就会马上调用invalidate()进行再次绘制。换个角度来看,我们其实就是在循环的调用小方块的move()方法和onDraw()方法来创建一个有趣的动画。
提示:
在onDraw()方法里面调用invalidate()方法来更新view的位置是创建动画的一种很简单的途径。如果做一个小游戏,用这个方式可以实现循环
源码大家去github上下载
点击打开链接待续。。。。