package com.example.draw2d; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.view.View; public class Draw2D extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new BallView(this)); } /** * 定义一个View,用来显示球 * @author siqi * */ public class BallView extends View { private Ball ball = new Ball(); private BallThread ballThread = new BallThread(this); public BallView(Context context) { super(context); /** * 启动线程 */ ballThread.start(); } public Ball getBall() { return ball; } /** * onDraw在每次View被刷新的时候执行。 */ @Override protected void onDraw(Canvas canvas) { ball.draw(canvas); super.onDraw(canvas); } } /** * 定义一个线程来刷新View,处理球的运动 * @author siqi * */ public class BallThread extends Thread { /** * 当bAlive是false的时候退出线程 */ private boolean bAlive = true; private BallView ballView; /** * 球运动的速度 */ private int speed = 3; /** * x方向的运动速度,正表示向右,负表示向左 */ private int xDir = speed; /** * y方向的运动速度,正表示向下,负表示向上 */ private int yDir = speed; /** * 我们需要在线程里面刷新view,和获取view中的那个球对象 * 所以我们需要传入view * @param view */ public BallThread(BallView view) { this.ballView = view; } /** * 线程运行 */ @Override public void run() { /** * 我们想要获取view的宽度和高度,但是在activity * setContentView(new BallView(this)); 后,获取的宽度和长度 * 有可能为0,设置view需要大约200ms的时间。所以在这里,我们用一个 * 循环,直到view准备好。 */ while(ballView.getWidth()<1) { try { Thread.sleep(100); } catch (InterruptedException e) { } } /** * 获取view的长度和宽度(不包括标题栏(有标题的那个)和通知栏(有信号图标的那个)) */ int screenWidth = ballView.getWidth(); int screenHeight = ballView.getHeight(); /** * 获取球的宽度 */ int ballW = ballView.getBall().getRect().width(); /** * 球下一个运动位置 */ int xNext, yNext; /** * 设置循环,我们需要有一个手段,能够结束线程,所以我们定义了bAlive * 当bAlive为false的时候,退出。 */ while(bAlive) { try { /** * 刷新一次view的显示后,都等待30ms。当图像每秒刷新30次的时候, * 大部分人的眼睛就已经认为图像是连续的了,1000ms/30 = 33.33ms,在这里 * 省略成30。 */ Thread.sleep(30); /** * 获取球当前位置,范围 */ Rect ballRect = ballView.getBall().getRect(); /** * 按照X,Y方向的速度,下一个位置 */ xNext = ballRect.left + xDir; yNext = ballRect.top + yDir; /** * 表示撞到了右边/左边的墙壁 */ if(xNext>=screenWidth-ballW || xNext<0) { xDir = -1 * xDir; //转向 xNext = ballRect.left + xDir; } /** * 表示撞到了下边/上边的墙壁 */ if(yNext>=screenHeight-ballW || yNext<0) { yDir = -1 * yDir; yNext = ballRect.top + yDir; } /** * 更新球的位置 */ ballView.ball.move(xNext, yNext); /** * 通知View,重画View */ ballView.post(new Runnable() { @Override public void run() { ballView.invalidate(); } }); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 调用destory,我们把bAlive设置成false,这样 * run()里面的循环就不会再继续。线程在执行完成后退出。 */ @Override public void destroy() { bAlive = false; } } /** * 定义球的类 * @author siqi * */ public class Ball { private ShapeDrawable mBall; public Ball() { this.mBall = createBall(10); } public Ball(int r) { this.mBall = createBall(r); } public Rect getRect() { if (this.mBall == null) { return null; } else { return this.mBall.getBounds(); } } /** * 创建一个球的绘图对象(Drawable),如果不用着色器的话,画出来的 * 球很难看,没有立体感。 * @param r 球的半径 * @return 返回球的绘图对象 */ private ShapeDrawable createBall(float r) { //直径 float diameter = 2 * r; //用椭圆模板创建一个绘图对象 ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); //设置球的颜色和深颜色,在这里,我们设置的是绿色RGB(128,255,30) //RGB值可以通过windows画图程序->颜色(菜单)->编辑颜色->自定义颜色得到 int red = 128; int green = 225; int blue = 30; int color = 0xff000000 | red << 16 | green << 8 | blue; //RGB各个颜色的值越小,颜色越黑(深) int darkColor = 0xff000000 | red/4 << 16 | green/4 << 8 | blue/4; //得到绘图对象的画笔 Paint paint = drawable.getPaint(); //创建着色器 RadialGradient gradient = new RadialGradient(r, r, r, color, darkColor, Shader.TileMode.CLAMP); //设置着色器 paint.setShader(gradient); //设置绘图对象区域 //注意,在android中(0,0,150,1)表示一条x轴范围为0-149,y=0的直线;并不像有些语言 //的实现,有些语言中(0,0,150,1)表示一个矩形,X范围:0-150,Y范围0-1。 drawable.setBounds(0, 0, (int)diameter, (int)diameter); return drawable; } /** * 将球移动到指定位置 * @param x 球的最左边X坐标,不是中心X坐标 * @param y 球的最上边Y坐标,不是中心Y坐标 */ public void move(int x, int y) { Rect rectOld = this.mBall.getBounds(); Rect rectNew = new Rect(rectOld); int relativeX = x - rectOld.left; int relativeY = y - rectOld.top; rectNew.left = rectNew.left + relativeX; rectNew.right = rectNew.right + relativeX; rectNew.top = rectNew.top + relativeY; rectNew.bottom = rectNew.bottom + relativeY; this.mBall.setBounds(rectNew); } /** * 在给定的画布上画球 * @param canvas 画布 */ public void draw(Canvas canvas) { this.mBall.draw(canvas); } } }
运行效果:
源代码下载: http://dom4j-android.googlecode.com/files/Draw2D.zip
参考的资料:
1.API Demo: com.example.android.apis.animation.AnimationCloning.java
2.http://developer.android.com/guide/topics/graphics/overview.html