如果想为自定义的UI控件添加动画效果,会发现动画相关的API是很有限的。那么有没有API可以直接向屏幕绘图呢?答案是肯定的。Android提供了Canvas满足这一要求。
在这个例子中,我们将以方块在屏幕上弹跳为例分析如何使用Canvas类在屏幕上绘制图形,并为其添加动画效果。应用程序最终效果如下:
我们先需要了解Canvas类的基本概念:“我们可以把Canvas视为Surface的替身或者接口,图形便是绘制在Surface上的。Canvas封装了所有绘图调用。通过Canvas,绘制到Surface上的内容首先存储到与之关联的Bitmap中,该Bitmap最终会呈现到窗口上。”
Activity代码如下:
package com.example.huangfei.hack;
import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
public class MainActivity extends Activity {
private DrawView mDrawView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取屏幕的宽和高
Display display = getWindowManager().getDefaultDisplay();
mDrawView = new DrawView(this);
mDrawView.width = display.getWidth();
mDrawView.height = display.getHeight();
//以DrawView作为Activity的内容视图,DrawView占据所有可用空间
setContentView(mDrawView);
}
}
DrawView类代码如下:
package com.example.huangfei.hack;
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);
/**
* 创建方块对象。Rectangle类内部实现了将自身绘制到Canvas上的逻辑,并且已经包含了正确变换其位置
* 的代码逻辑。
*/
mRectangle = new Rectangle(context, this);
mRectangle.setARGB(255, 255, 0, 0);
mRectangle.setSpeedX(3);
mRectangle.setSpeedY(3);
}
@Override
protected void onDraw(Canvas canvas) {
mRectangle.move();//变换方块位置
mRectangle.onDraw(canvas);//将方块绘制到Canvas上
/**
*invalidate()方法强制重绘视图。把该方法放在onDraw()的目的是为了在View绘制完自身后,可以立即重新
* 调用onDraw()。换句话说,通过循环调用Rectangle的move()和onDraw()方法实现了一个动画效果。
*/
invalidate();
}
}
Rectangle类代码如下:
package com.example.huangfei.hack;
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 {
private static final int MAX_SIZE = 40;//方块距右端或底部的距离,一般为方块的大小mRealSize
private static final int ALPHA = 255;//画笔的透明度
private DrawView mDrawView;
private Paint mInnerPaint;//画笔
private RectF mDrawFect;//矩形坐标
private float mCoordX;//方块当前x轴位置
private float mCoordY;//方块当前Y轴位置
private int mRealSize = 40;//方块的大小
private int mSpeedX = 3;//方块x轴每次移动的距离
private int mSpeedY = 3;//方块轴每次移动的距离
private boolean goRight = true;//是否向右移动
private boolean goDown = true;//是否向下移动
public Rectangle(Context context, DrawView drawView) {
super(context);
mDrawView = drawView;
mInnerPaint = new Paint();
mDrawFect = new RectF();
//设置画笔的默认颜色为红色
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);
}
@Override
public float getX() {
return mCoordX;
}
@Override
public void setX(float x) {
mCoordX = x;
}
@Override
public float getY() {
return mCoordY;
}
@Override
public void setY(float y) {
mCoordY = y;
}
public int getSize() {
return mRealSize;
}
public void setSize(int realSize) {
mRealSize = realSize;
}
public int getSpeedX() {
return mSpeedX;
}
public void setSpeedX(int speedX) {
mSpeedX = speedX;
}
public int getSpeedY() {
return mSpeedY;
}
public void setSpeedY(int speedY) {
mSpeedY = speedY;
}
public void move() {
moveTo(mSpeedX, mSpeedY);
}
private void moveTo(int goX, int goY){
//判断X轴方向的边界
if(mCoordX > mDrawView.width - MAX_SIZE){
goRight = false;
}
if(mCoordX < 0){
goRight = true;
}
//判断Y轴方向的边界
if(mCoordY > mDrawView.height - MAX_SIZE){
goDown = false;
}
if(mCoordY < 0){
goDown = true;
}
//移动方块
if(goRight){
mCoordX += goX;
}else{
mCoordX -= goX;
}
if(goDown){
mCoordY += goY;
}else{
mCoordY -= goY;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawFect.set(mCoordX, mCoordY, mCoordX + mRealSize, mCoordY + mRealSize);
canvas.drawRoundRect(mDrawFect, 0, 0, mInnerPaint);
}
}
在onDraw()方法中通过调用invalidate()方法变换视图的位置是实现自定义动画的简单方法。如果你想开发一款小游戏,使用这个小技巧处理游戏的主循环是一个简单的方法。