转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42965779 ,本文出自:【张鸿洋的博客】

1、概述

哈,记得以前写过Android SurfaceView实战 打造抽奖转盘 , 同属于SurfaceView系列,基本可以从这篇博文中学习到SurfaceView的用法,以及利用SurfaceView做抽奖转盘。但是其中缺少一部分的知识点,就是与用户交互时界面的改变,所以今天给大家再带来本篇博文教大家如何做flabby bird这款游戏,这游戏虽然不难,但是也为其作者挣了不少钱,大家在学会以后,可以尽可能发挥自己的创意,做属于自己的游戏,说不定下一个火的奏是你。

ok,那么首先上下效果图:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

再来张动态的:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

由于上传图片最大限制为2M,所以做了压缩处理,凑合看吧 ~~~

2、分析

仔细观察游戏,需要绘制的有:背景、地板、鸟、管道、分数;

游戏开始时:

地板给人一种想左移动的感觉;

管道与地板同样的速度向左移动;

鸟默认下落;

当用户touch屏幕时,鸟上升一段距离后,下落;

运动过程中需要判断管道和鸟之间的位置关系,是否触碰,是否穿过等,需要计算分数。

好了,大概就这么多,那我们首先开始考虑绘制~~~

3、SurfaceView的一般写法

接下来,我们首先编写下SurfaceView的一般写法:

  1. package com.zhy.view;
  2. import android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.PixelFormat;
  5. import android.util.AttributeSet;
  6. import android.view.SurfaceHolder;
  7. import android.view.SurfaceHolder.Callback;
  8. import android.view.SurfaceView;
  9. public class GameFlabbyBird extends SurfaceView implements Callback, Runnable
  10. {
  11. private SurfaceHolder mHolder;
  12. /**
  13. * 与SurfaceHolder绑定的Canvas
  14. */
  15. private Canvas mCanvas;
  16. /**
  17. * 用于绘制的线程
  18. */
  19. private Thread t;
  20. /**
  21. * 线程的控制开关
  22. */
  23. private boolean isRunning;
  24. public GameFlabbyBird(Context context)
  25. {
  26. this(context, null);
  27. }
  28. public GameFlabbyBird(Context context, AttributeSet attrs)
  29. {
  30. super(context, attrs);
  31. mHolder = getHolder();
  32. mHolder.addCallback(this);
  33. setZOrderOnTop(true);// 设置画布 背景透明
  34. mHolder.setFormat(PixelFormat.TRANSLUCENT);
  35. // 设置可获得焦点
  36. setFocusable(true);
  37. setFocusableInTouchMode(true);
  38. // 设置常亮
  39. this.setKeepScreenOn(true);
  40. }
  41. @Override
  42. public void surfaceCreated(SurfaceHolder holder)
  43. {
  44. // 开启线程
  45. isRunning = true;
  46. t = new Thread(this);
  47. t.start();
  48. }
  49. @Override
  50. public void surfaceChanged(SurfaceHolder holder, int format, int width,
  51. int height)
  52. {
  53. // TODO Auto-generated method stub
  54. }
  55. @Override
  56. public void surfaceDestroyed(SurfaceHolder holder)
  57. {
  58. // 通知关闭线程
  59. isRunning = false;
  60. }
  61. @Override
  62. public void run()
  63. {
  64. while (isRunning)
  65. {
  66. long start = System.currentTimeMillis();
  67. draw();
  68. long end = System.currentTimeMillis();
  69. try
  70. {
  71. if (end - start < 50)
  72. {
  73. Thread.sleep(50 - (end - start));
  74. }
  75. } catch (InterruptedException e)
  76. {
  77. e.printStackTrace();
  78. }
  79. }
  80. }
  81. private void draw()
  82. {
  83. try
  84. {
  85. // 获得canvas
  86. mCanvas = mHolder.lockCanvas();
  87. if (mCanvas != null)
  88. {
  89. // drawSomething..
  90. }
  91. } catch (Exception e)
  92. {
  93. } finally
  94. {
  95. if (mCanvas != null)
  96. mHolder.unlockCanvasAndPost(mCanvas);
  97. }
  98. }
  99. }

这个基础的类,在Android SurfaceView实战 打造抽奖转盘已经出现过,就不多说了,大家以后写SurfaceView的相关程序,可以直接拷贝,在此类基础上编写。

4、绘制

1、绘制背景

最简单的当然是背景了,直接drawBitmap即可。

我们添加需要的成员变量,以及初始化一些参数,然后添加drawBg方法,最后在draw中调用drawBg;

  1. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
  2. Runnable
  3. {
  4. /**
  5. * 当前View的尺寸
  6. */
  7. private int mWidth;
  8. private int mHeight;
  9. private RectF mGamePanelRect = new RectF();
  10. /**
  11. * 背景
  12. */
  13. private Bitmap mBg;
  14. public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)
  15. {
  16. //省略了很多代码
  17. initBitmaps();
  18. }
  19. /**
  20. * 初始化图片
  21. */
  22. private void initBitmaps()
  23. {
  24. mBg = loadImageByResId(R.drawable.bg1);
  25. }
  26. private void draw()
  27. {
  28. //省略了很多代码
  29. drawBg();
  30. //省略了很多代码
  31. }
  32. /**
  33. * 绘制背景
  34. */
  35. private void drawBg()
  36. {
  37. mCanvas.drawBitmap(mBg, null, mGamePanelRect, null);
  38. }
  39. /**
  40. * 初始化尺寸相关
  41. */
  42. @Override
  43. protected void onSizeChanged(int w, int h, int oldw, int oldh)
  44. {
  45. super.onSizeChanged(w, h, oldw, oldh);
  46. mWidth = w;
  47. mHeight = h;
  48. mGamePanelRect.set(0, 0, w, h);
  49. }
  50. /**
  51. * 根据resId加载图片
  52. *
  53. * @param resId
  54. * @return
  55. */
  56. private Bitmap loadImageByResId(int resId)
  57. {
  58. return BitmapFactory.decodeResource(getResources(), resId);
  59. }
  60. }

基本就是添加成员变量,然后初始化,然后绘制,上述代码经过删减,贴出的都是与前面基础代码不同的部分,大家可以将代码对号入座进行填充。

好了,现在背景图绘制好了,接下来,我们绘制小鸟~~~

2、绘制bird

鸟在我们的屏幕中,初始化时需要一个位置,x上,肯定是居中,y上我们取2/3的高度;

关于bird,我们单独创建一个类:

  1. package com.zhy.view;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.RectF;
  6. public class Bird
  7. {
  8. /**
  9. * 鸟在屏幕高度的2/3位置
  10. */
  11. private static final float RADIO_POS_HEIGHT = 2 / 3F;
  12. /**
  13. * 鸟的宽度 30dp
  14. */
  15. private static final int BIRD_SIZE = 30;
  16. /**
  17. * 鸟的横坐标
  18. */
  19. private int x;
  20. /**
  21. * 鸟的纵坐标
  22. */
  23. private int y;
  24. /**
  25. * 鸟的宽度
  26. */
  27. private int mWidth;
  28. /**
  29. * 鸟的高度
  30. */
  31. private int mHeight;
  32. /**
  33. * 鸟的bitmap
  34. */
  35. private Bitmap bitmap;
  36. /**
  37. * 鸟绘制的范围
  38. */
  39. private RectF rect = new RectF();
  40. public Bird(Context context, int gameWith, int gameHeight, Bitmap bitmap)
  41. {
  42. this.bitmap = bitmap;
  43. //鸟的位置
  44. x = gameWith / 2 - bitmap.getWidth() / 2;
  45. y = (int) (gameHeight * RADIO_POS_HEIGHT);
  46. // 计算鸟的宽度和高度
  47. mWidth = Util.dp2px(context, BIRD_SIZE);
  48. mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());
  49. }
  50. /**
  51. * 绘制自己
  52. *
  53. * @param canvas
  54. */
  55. public void draw(Canvas canvas)
  56. {
  57. rect.set(x, y, x + mWidth, y + mHeight);
  58. canvas.drawBitmap(bitmap, null, rect, null);
  59. }
  60. public int getY()
  61. {
  62. return y;
  63. }
  64. public void setY(int y)
  65. {
  66. this.y = y;
  67. }
  68. public int getWidth()
  69. {
  70. return mWidth;
  71. }
  72. public int getHeight()
  73. {
  74. return mHeight;
  75. }
  76. }

定义了一个类,代表我们的鸟,以及一堆成员变量,并且提供一个draw方法对外;

在GameFlabbyBird中,只需要,初始化我们的Bird,在draw里面调用bird.draw即可;

部分筛检后代码:

  1. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
  2. Runnable
  3. {
  4. /**
  5. * *********鸟相关**********************
  6. */
  7. private Bird mBird;
  8. private Bitmap mBirdBitmap;
  9. /**
  10. * 初始化图片
  11. */
  12. private void initBitmaps()
  13. {
  14. mBg = loadImageByResId(R.drawable.bg1);
  15. mBirdBitmap = loadImageByResId(R.drawable.b1);
  16. }
  17. private void draw()
  18. {
  19. // drawSomething..
  20. drawBg();
  21. drawBird();
  22. }
  23. private void drawBird()
  24. {
  25. mBird.draw(mCanvas);
  26. }
  27. /**
  28. * 初始化尺寸相关
  29. */
  30. @Override
  31. protected void onSizeChanged(int w, int h, int oldw, int oldh)
  32. {
  33. // 初始化mBird
  34. mBird = new Bird(getContext(), mWidth, mHeight, mBirdBitmap);
  35. }
  36. }

是不是很简单,下面看下此时效果图:

Activity里面这么调用即可:

  1. package com.zhy.surfaceViewDemo;
  2. import com.zhy.view.GameFlabbyBird;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.view.Window;
  6. import android.view.WindowManager;
  7. public class MainActivity extends Activity
  8. {
  9. GameFlabbyBird mGame;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState)
  12. {
  13. super.onCreate(savedInstanceState);
  14. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  15. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  16. requestWindowFeature(Window.FEATURE_NO_TITLE);
  17. mGame = new GameFlabbyBird(this);
  18. setContentView(mGame);
  19. }
  20. }

效果图:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

不管咋样,我们的鸟已经在指定的位置了~~~有木有一点小激动~~

下面开始添加地板;

3、绘制地板

绘制地板相比来说会难一点,因为我们需要考虑怎么让地板运动,起初我截取了两个大图,希望通过两张图不断变化,产生动画效果,but,动画的太卡,有跳跃感;

于是,我忽然想到了一个东西可以做,我就把基础图变成了这样:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

很小的一块图,先不考虑运动,如何填充成我们目标效果呢?

还记得有个类叫做BitmapShader么?我们可以利用它进行填充。

相关知识可以参考:Android BitmapShader 实战 实现圆形、圆角图片

首先我们依旧是定义一个地板类:Floor

  1. package com.zhy.view;
  2. import java.util.concurrent.TimeUnit;
  3. import com.zhy.surfaceViewDemo.Config;
  4. import android.content.Context;
  5. import android.graphics.Bitmap;
  6. import android.graphics.BitmapShader;
  7. import android.graphics.Canvas;
  8. import android.graphics.Paint;
  9. import android.graphics.Paint.Style;
  10. import android.graphics.Shader.TileMode;
  11. public class Floor
  12. {
  13. /*
  14. * 地板位置游戏面板高度的4/5到底部
  15. */
  16. private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5
  17. /**
  18. * x坐标
  19. */
  20. private int x;
  21. /**
  22. * y坐标
  23. */
  24. private int y;
  25. /**
  26. * 填充物
  27. */
  28. private BitmapShader mFloorShader;
  29. private int mGameWidth;
  30. private int mGameHeight;
  31. public Floor(int gameWidth, int gameHeight, Bitmap floorBg)
  32. {
  33. mGameWidth = gameWidth;
  34. mGameHeight = gameHeight;
  35. y = (int) (gameHeight * FLOOR_Y_POS_RADIO);
  36. mFloorShader = new BitmapShader(floorBg, TileMode.REPEAT,
  37. TileMode.CLAMP);
  38. }
  39. /**
  40. * 绘制自己
  41. *
  42. * @param mCanvas
  43. * @param mPaint
  44. */
  45. public void draw(Canvas mCanvas, Paint mPaint)
  46. {
  47. if (-x > mGameWidth)
  48. {
  49. x = x % mGameWidth;
  50. }
  51. mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
  52. //移动到指定的位置
  53. mCanvas.translate(x, y);
  54. mPaint.setShader(mFloorShader);
  55. mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);
  56. mCanvas.restore();
  57. mPaint.setShader(null);
  58. }
  59. public int getX()
  60. {
  61. return x;
  62. }
  63. public void setX(int x)
  64. {
  65. this.x = x;
  66. }
  67. }

定义了一堆成员变量,核心就在于,我们传入地板背景的填充物,然后初始化我们的mFloorShader,横向重复,纵向拉伸(这里的拉伸是指,纵向的最后一个像素不断重复)。

我们对外公布了draw方法,传入Canvas,我们首先调用canvas.save(),然后将canvas移动到指定的位置,然后绘制我们的矩形,矩形的填充就是我们的地板了~~;

这里,注意一下,我们这里使用了一个变量x,而不是0;为什么呢?因为我们的地板需要利用这个x运动。

那么现在我们如何才能动呢?

首先我们在GameFlabbyBird定义一个变量,表示移动速度mSpeed,然后在draw中不断更新mFloor的x坐标为:mFloor.setX(mFloor.getX() - mSpeed);

这样的画,每次绘制我们floor的起点,会向左移动mSpeed个位置,就形成了运行的效果;但是呢?不能一直减下去,不然最终我们的x岂不是负无穷了,那得绘制多大?

所以我们:

if (-x > mGameWidth)
{
x = x % mGameWidth;
}

如果x的正值大于宽度了,我们取余一下~~~

最终我们的绘制范围是:

mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);

ok,贴下筛检后GameFlabbyBird代码:

  1. package com.zhy.view;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Canvas;
  6. import android.graphics.Paint;
  7. import android.graphics.PixelFormat;
  8. import android.graphics.RectF;
  9. import android.util.AttributeSet;
  10. import android.util.Log;
  11. import android.view.SurfaceHolder;
  12. import android.view.SurfaceHolder.Callback;
  13. import android.view.SurfaceView;
  14. import com.zhy.surfaceViewDemo.R;
  15. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
  16. Runnable
  17. {
  18. private Paint mPaint;
  19. /**
  20. * 地板
  21. */
  22. private Floor mFloor;
  23. private Bitmap mFloorBg;
  24. private int mSpeed;
  25. public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)
  26. {
  27. super(context, attrs);
  28. mPaint = new Paint();
  29. mPaint.setAntiAlias(true);
  30. mPaint.setDither(true);
  31. initBitmaps();
  32. // 初始化速度
  33. mSpeed = Util.dp2px(getContext(), 2);
  34. }
  35. /**
  36. * 初始化图片
  37. */
  38. private void initBitmaps()
  39. {
  40. mFloorBg = loadImageByResId(R.drawable.floor_bg2);
  41. }
  42. private void draw()
  43. {
  44. // drawSomething..
  45. drawBg();
  46. drawBird();
  47. drawFloor();
  48. // 更新我们地板绘制的x坐标
  49. mFloor.setX(mFloor.getX() - mSpeed);
  50. }
  51. private void drawFloor()
  52. {
  53. mFloor.draw(mCanvas, mPaint);
  54. }
  55. @Override
  56. protected void onSizeChanged(int w, int h, int oldw, int oldh)
  57. {
  58. // 初始化地板
  59. mFloor = new Floor(mWidth, mHeight, mFloorBg);
  60. }
  61. }

其实很简单,就是声明几个变量,初始化一下;记得在draw中更新mFloor的x即可。

现在的效果:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

好了,最后剩下个管道了~~~

4、绘制管道

然后是写搞一个管道类Pipe,注意我们的管道分为上下,每个管道的高度可能不同,所以会多一些成员变量;

  1. package com.zhy.view;
  2. import java.util.Random;
  3. import android.content.Context;
  4. import android.graphics.Bitmap;
  5. import android.graphics.Canvas;
  6. import android.graphics.RectF;
  7. /**
  8. * 管道分为上下
  9. *
  10. * @author zhy
  11. *
  12. */
  13. public class Pipe
  14. {
  15. /**
  16. * 上下管道间的距离
  17. */
  18. private static final float RADIO_BETWEEN_UP_DOWN = 1 / 5F;
  19. /**
  20. * 上管道的最大高度
  21. */
  22. private static final float RADIO_MAX_HEIGHT = 2 / 5F;
  23. /**
  24. * 上管道的最小高度
  25. */
  26. private static final float RADIO_MIN_HEIGHT = 1 / 5F;
  27. /**
  28. * 管道的横坐标
  29. */
  30. private int x;
  31. /**
  32. * 上管道的高度
  33. */
  34. private int height;
  35. /**
  36. * 上下管道间的距离
  37. */
  38. private int margin;
  39. /**
  40. * 上管道图片
  41. */
  42. private Bitmap mTop;
  43. /**
  44. * 下管道图片
  45. */
  46. private Bitmap mBottom;
  47. private static Random random = new Random();
  48. public Pipe(Context context, int gameWidth, int gameHeight, Bitmap top,
  49. Bitmap bottom)
  50. {
  51. margin = (int) (gameHeight * RADIO_BETWEEN_UP_DOWN);
  52. // 默认从最左边出现
  53. x = gameWidth;
  54. mTop = top;
  55. mBottom = bottom;
  56. randomHeight(gameHeight);
  57. }
  58. /**
  59. * 随机生成一个高度
  60. */
  61. private void randomHeight(int gameHeight)
  62. {
  63. height = random
  64. .nextInt((int) (gameHeight * (RADIO_MAX_HEIGHT - RADIO_MIN_HEIGHT)));
  65. height = (int) (height + gameHeight * RADIO_MIN_HEIGHT);
  66. }
  67. public void draw(Canvas mCanvas, RectF rect)
  68. {
  69. mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
  70. // rect为整个管道,假设完整管道为100,需要绘制20,则向上偏移80
  71. mCanvas.translate(x, -(rect.bottom - height));
  72. mCanvas.drawBitmap(mTop, null, rect, null);
  73. // 下管道,便宜量为,上管道高度+margin
  74. mCanvas.translate(0, (rect.bottom - height) + height + margin);
  75. mCanvas.drawBitmap(mBottom, null, rect, null);
  76. mCanvas.restore();
  77. }
  78. public int getX()
  79. {
  80. return x;
  81. }
  82. public void setX(int x)
  83. {
  84. this.x = x;
  85. }
  86. }

我们直接看draw方法,我们的传入的rect是固定的一个矩形,我们的上下管道都是完整的绘制在这个rect中;

然后根据height,去偏移canvas的y,让rect显示出height部分,主要是因为,这样可以保证每个管道样子是一样的(如果根据height,使用不同的rect,会产生缩放);

Pipe写好了~~我们需要在GameFlabbyBird中去使用;但是考虑一下,游戏中的管道不像鸟和地面,有很多个,且是在运行中不断生成新的~~~

所以我们保存Pipe最起码是个List<Pipe>

筛检后的代码:

  1. public class GameFlabbyBird extends SurfaceView implements Callback, Runnable
  2. {
  3. /**
  4. * *********管道相关**********************
  5. */
  6. /**
  7. * 管道
  8. */
  9. private Bitmap mPipeTop;
  10. private Bitmap mPipeBottom;
  11. private RectF mPipeRect;
  12. private int mPipeWidth;
  13. /**
  14. * 管道的宽度 60dp
  15. */
  16. private static final int PIPE_WIDTH = 60;
  17. private List<Pipe> mPipes = new ArrayList<Pipe>();
  18. public GameFlabbyBird(Context context, AttributeSet attrs)
  19. {
  20. super(context, attrs);
  21. mPipeWidth = Util.dp2px(getContext(), PIPE_WIDTH);
  22. }
  23. /**
  24. * 初始化图片
  25. */
  26. private void initBitmaps()
  27. {
  28. ;
  29. mPipeTop = loadImageByResId(R.drawable.g2);
  30. mPipeBottom = loadImageByResId(R.drawable.g1);
  31. }
  32. private void draw()
  33. {
  34. drawBg();
  35. drawBird();
  36. drawPipes();
  37. drawFloor();
  38. }
  39. /**
  40. * 绘制管道
  41. */
  42. private void drawPipes()
  43. {
  44. for (Pipe pipe : mPipes)
  45. {
  46. pipe.setX(pipe.getX() - mSpeed);
  47. pipe.draw(mCanvas, mPipeRect);
  48. }
  49. }
  50. /**
  51. * 初始化尺寸相关
  52. */
  53. @Override
  54. protected void onSizeChanged(int w, int h, int oldw, int oldh)
  55. {
  56. super.onSizeChanged(w, h, oldw, oldh);
  57. // 初始化管道范围
  58. mPipeRect = new RectF(0, 0, mPipeWidth, mHeight);
  59. Pipe pipe = new Pipe(getContext(), w, h, mPipeTop, mPipeBottom);
  60. mPipes.add(pipe);
  61. }
  62. }

我们在onSizeChanged中初始化了一个Pipe,添加到了mPipes中,然后在draw里面,动态改变Pipe的x为pipe.setX(pipe.getX() - mSpeed);

下面来看下效果:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

我们的管道从右侧进入界面,然后消失在左侧~

当然了,关于管道还有很多需要编写,比如管道每隔多远生成一个,也不能让无限生成,当管道从界面移除应该从mPipes中移出;

以及判断管道和鸟的碰撞,这些都放置到下一篇博客叙述~~

5、绘制分数

分数的绘制比较简单,我准备了10个图,对应于0-9

没有单独定义类了,直接写了~~

筛检后的代码:

  1. public class GameFlabbyBird extends SurfaceView implements Callback,
  2. Runnable
  3. {
  4. /**
  5. * 分数
  6. */
  7. private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1,
  8. R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5,
  9. R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 };
  10. private Bitmap[] mNumBitmap;
  11. private int mGrade = 100;
  12. /**
  13. * 单个数字的高度的1/15
  14. */
  15. private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f;
  16. /**
  17. * 单个数字的宽度
  18. */
  19. private int mSingleGradeWidth;
  20. /**
  21. * 单个数字的高度
  22. */
  23. private int mSingleGradeHeight;
  24. /**
  25. * 单个数字的范围
  26. */
  27. private RectF mSingleNumRectF;
  28. /**
  29. * 初始化图片
  30. */
  31. private void initBitmaps()
  32. {
  33. mNumBitmap = new Bitmap[mNums.length];
  34. for (int i = 0; i < mNumBitmap.length; i++)
  35. {
  36. mNumBitmap[i] = loadImageByResId(mNums[i]);
  37. }
  38. }
  39. private void draw()
  40. {
  41. // drawSomething..
  42. drawBg();
  43. drawBird();
  44. drawPipes();
  45. drawFloor();
  46. drawGrades();
  47. }
  48. /**
  49. * 绘制分数
  50. */
  51. private void drawGrades()
  52. {
  53. String grade = mGrade + "";
  54. mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
  55. mCanvas.translate(mWidth / 2 - grade.length() * mSingleGradeWidth / 2,
  56. 1f / 8 * mHeight);
  57. // draw single num one by one
  58. for (int i = 0; i < grade.length(); i++)
  59. {
  60. String numStr = grade.substring(i, i + 1);
  61. int num = Integer.valueOf(numStr);
  62. mCanvas.drawBitmap(mNumBitmap[num], null, mSingleNumRectF, null);
  63. mCanvas.translate(mSingleGradeWidth, 0);
  64. }
  65. mCanvas.restore();
  66. }
  67. /**
  68. * 初始化尺寸相关
  69. */
  70. @Override
  71. protected void onSizeChanged(int w, int h, int oldw, int oldh)
  72. {
  73. super.onSizeChanged(w, h, oldw, oldh);
  74. // 初始化分数
  75. mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);
  76. mSingleGradeWidth = (int) (mSingleGradeHeight * 1.0f
  77. / mNumBitmap[0].getHeight() * mNumBitmap[0].getWidth());
  78. mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight);
  79. }
  80. }

我们定义了单个数字的范围,然后假设现在为100分,注意在绘制的时候,直接提取数字,把数字作为下标,找到对于的图片进行绘制;

绘制前,根据数字的位数,对画布进行偏移到中心位置,然后绘制;绘制过程中,每绘制完成一个数字则偏移一个数字的宽度;

现在的效果:

Android SurfaceView实战 带你玩转flabby bird (上)-LMLPHP

ok,到此为止,我们完成了所有需要绘制的东西~~由于篇幅原因,下一篇,将在此基础上完善剩下的所有内容~~~

有兴趣的,可以在此基础上直接尝试写了~~~

源码点击下载

05-02 09:00