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


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

    1、概述

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

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


    再来张动态的:


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


    2、分析

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

    游戏开始时:

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

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

    鸟默认下落;

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

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

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


    3、SurfaceView的一般写法

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

    package com.zhy.view;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.PixelFormat;
    import android.util.AttributeSet;
    import android.view.SurfaceHolder;
    import android.view.SurfaceHolder.Callback;
    import android.view.SurfaceView;
    
    public class GameFlabbyBird extends SurfaceView implements Callback, Runnable
    {
    
    	private SurfaceHolder mHolder;
    	/**
    	 * 与SurfaceHolder绑定的Canvas
    	 */
    	private Canvas mCanvas;
    	/**
    	 * 用于绘制的线程
    	 */
    	private Thread t;
    	/**
    	 * 线程的控制开关
    	 */
    	private boolean isRunning;
    
    	public GameFlabbyBird(Context context)
    	{
    		this(context, null);
    	}
    
    	public GameFlabbyBird(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    
    		mHolder = getHolder();
    		mHolder.addCallback(this);
    
    		setZOrderOnTop(true);// 设置画布 背景透明
    		mHolder.setFormat(PixelFormat.TRANSLUCENT);
    
    		// 设置可获得焦点
    		setFocusable(true);
    		setFocusableInTouchMode(true);
    		// 设置常亮
    		this.setKeepScreenOn(true);
    
    	}
    
    	@Override
    	public void surfaceCreated(SurfaceHolder holder)
    	{
    
    		// 开启线程
    		isRunning = true;
    		t = new Thread(this);
    		t.start();
    	}
    
    	@Override
    	public void surfaceChanged(SurfaceHolder holder, int format, int width,
    			int height)
    	{
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void surfaceDestroyed(SurfaceHolder holder)
    	{
    		// 通知关闭线程
    		isRunning = false;
    	}
    
    	@Override
    	public void run()
    	{
    		while (isRunning)
    		{
    			long start = System.currentTimeMillis();
    			draw();
    			long end = System.currentTimeMillis();
    
    			try
    			{
    				if (end - start < 50)
    				{
    					Thread.sleep(50 - (end - start));
    				}
    			} catch (InterruptedException e)
    			{
    				e.printStackTrace();
    			}
    
    		}
    
    	}
    
    	private void draw()
    	{
    		try
    		{
    			// 获得canvas
    			mCanvas = mHolder.lockCanvas();
    			if (mCanvas != null)
    			{
    				// drawSomething..
    			}
    		} catch (Exception e)
    		{
    		} finally
    		{
    			if (mCanvas != null)
    				mHolder.unlockCanvasAndPost(mCanvas);
    		}
    	}
    }
    

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


    4、绘制


    1、绘制背景

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

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

    public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
    		Runnable
    {
    	/**
    	 * 当前View的尺寸
    	 */
    	private int mWidth;
    	private int mHeight;
    	private RectF mGamePanelRect = new RectF();
    
    	/**
    	 * 背景
    	 */
    	private Bitmap mBg;
    
    	public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)
    	{
    		//省略了很多代码
    		initBitmaps();
    	}
    
    	/**
    	 * 初始化图片
    	 */
    	private void initBitmaps()
    	{
    		mBg = loadImageByResId(R.drawable.bg1);
    	}
    
    	private void draw()
    	{
    		//省略了很多代码
    		drawBg();
    		//省略了很多代码
    	}
    
    	/**
    	 * 绘制背景
    	 */
    	private void drawBg()
    	{
    		mCanvas.drawBitmap(mBg, null, mGamePanelRect, null);
    	}
    
    	/**
    	 * 初始化尺寸相关
    	 */
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh)
    	{
    		super.onSizeChanged(w, h, oldw, oldh);
    
    		mWidth = w;
    		mHeight = h;
    		mGamePanelRect.set(0, 0, w, h);
    	}
    
    	/**
    	 * 根据resId加载图片
    	 * 
    	 * @param resId
    	 * @return
    	 */
    	private Bitmap loadImageByResId(int resId)
    	{
    		return BitmapFactory.decodeResource(getResources(), resId);
    	}
    }
    

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

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


    2、绘制bird

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

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

    package com.zhy.view;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.RectF;
    
    public class Bird
    {
    	/**
    	 * 鸟在屏幕高度的2/3位置
    	 */
    	private static final float RADIO_POS_HEIGHT = 2 / 3F;
    	/**
    	 * 鸟的宽度 30dp
    	 */
    	private static final int BIRD_SIZE = 30;
    
    	/**
    	 * 鸟的横坐标
    	 */
    	private int x;
    	/**
    	 * 鸟的纵坐标
    	 */
    	private int y;
    	/**
    	 * 鸟的宽度
    	 */
    	private int mWidth;
    	/**
    	 * 鸟的高度
    	 */
    	private int mHeight;
    
    	/**
    	 * 鸟的bitmap
    	 */
    	private Bitmap bitmap;
    	/**
    	 * 鸟绘制的范围
    	 */
    	private RectF rect = new RectF();
    
    	public Bird(Context context, int gameWith, int gameHeight, Bitmap bitmap)
    	{
    
    		this.bitmap = bitmap;
    		//鸟的位置
    		x = gameWith / 2 - bitmap.getWidth() / 2;
    		y = (int) (gameHeight * RADIO_POS_HEIGHT);
    
    		// 计算鸟的宽度和高度
    		mWidth = Util.dp2px(context, BIRD_SIZE);
    		mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());
    	}
    
    	/**
    	 * 绘制自己
    	 * 
    	 * @param canvas
    	 */
    	public void draw(Canvas canvas)
    	{
    		rect.set(x, y, x + mWidth, y + mHeight);
    		canvas.drawBitmap(bitmap, null, rect, null);
    
    	}
    
    	public int getY()
    	{
    		return y;
    	}
    
    	public void setY(int y)
    	{
    		this.y = y;
    	}
    
    	public int getWidth()
    	{
    		return mWidth;
    	}
    
    	public int getHeight()
    	{
    		return mHeight;
    	}
    
    }
    

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

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

    部分筛检后代码:

    public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
    		Runnable
    {
    	/**
    	 * *********鸟相关**********************
    	 */
    	private Bird mBird;
    	private Bitmap mBirdBitmap;
    
    	/**
    	 * 初始化图片
    	 */
    	private void initBitmaps()
    	{
    		mBg = loadImageByResId(R.drawable.bg1);
    		mBirdBitmap = loadImageByResId(R.drawable.b1);
    
    	}
    
    	private void draw()
    	{
    		// drawSomething..
    
    		drawBg();
    		drawBird();
    
    	}
    
    	private void drawBird()
    	{
    		mBird.draw(mCanvas);
    	}
    
    	/**
    	 * 初始化尺寸相关
    	 */
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh)
    	{
    		// 初始化mBird
    		mBird = new Bird(getContext(), mWidth, mHeight, mBirdBitmap);
    
    	}
    
    }
    
    是不是很简单,下面看下此时效果图:

    Activity里面这么调用即可:

    package com.zhy.surfaceViewDemo;
    
    import com.zhy.view.GameFlabbyBird;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Window;
    import android.view.WindowManager;
    
    public class MainActivity extends Activity
    {
    	GameFlabbyBird mGame;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    				WindowManager.LayoutParams.FLAG_FULLSCREEN);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		mGame = new GameFlabbyBird(this);
    		setContentView(mGame);
    
    	}
    
    }
    

    效果图:

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

    下面开始添加地板;

    3、绘制地板

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

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

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

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

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

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

    package com.zhy.view;
    
    import java.util.concurrent.TimeUnit;
    
    import com.zhy.surfaceViewDemo.Config;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Paint.Style;
    import android.graphics.Shader.TileMode;
    
    public class Floor
    {
    	/*
    	 * 地板位置游戏面板高度的4/5到底部
    	 */
    	private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5
    
    	/**
    	 * x坐标
    	 */
    	private int x;
    	/**
    	 * y坐标
    	 */
    	private int y;
    	/**
    	 * 填充物
    	 */
    	private BitmapShader mFloorShader;
    
    	private int mGameWidth;
    
    	private int mGameHeight;
    
    	public Floor(int gameWidth, int gameHeight, Bitmap floorBg)
    	{
    		mGameWidth = gameWidth;
    		mGameHeight = gameHeight;
    		y = (int) (gameHeight * FLOOR_Y_POS_RADIO);
    		mFloorShader = new BitmapShader(floorBg, TileMode.REPEAT,
    				TileMode.CLAMP);
    	}
    
    	/**
    	 * 绘制自己
    	 * 
    	 * @param mCanvas
    	 * @param mPaint
    	 */
    	public void draw(Canvas mCanvas, Paint mPaint)
    	{
    		if (-x > mGameWidth)
    		{
    			x = x % mGameWidth;
    		}
    		mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
    		//移动到指定的位置
    		mCanvas.translate(x, y);
    		mPaint.setShader(mFloorShader);
    		mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);
    		mCanvas.restore();
    		mPaint.setShader(null);
    	}
    
    	public int getX()
    	{
    		return x;
    	}
    
    	public void setX(int x)
    	{
    		this.x = x;
    	}
    
    }
    

    定义了一堆成员变量,核心就在于,我们传入地板背景的填充物,然后初始化我们的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代码:

    package com.zhy.view;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.PixelFormat;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceHolder.Callback;
    import android.view.SurfaceView;
    
    import com.zhy.surfaceViewDemo.R;
    
    public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
    		Runnable
    {
    	private Paint mPaint;
    	/**
    	 * 地板
    	 */
    	private Floor mFloor;
    	private Bitmap mFloorBg;
    
    	private int mSpeed;
    
    	public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    
    		mPaint = new Paint();
    		mPaint.setAntiAlias(true);
    		mPaint.setDither(true);
    
    		initBitmaps();
    
    		// 初始化速度
    		mSpeed = Util.dp2px(getContext(), 2);
    
    	}
    
    	/**
    	 * 初始化图片
    	 */
    	private void initBitmaps()
    	{
    		mFloorBg = loadImageByResId(R.drawable.floor_bg2);
    
    	}
    
    	private void draw()
    	{
    
    		// drawSomething..
    
    		drawBg();
    		drawBird();
    		drawFloor();
    
    		// 更新我们地板绘制的x坐标
    		mFloor.setX(mFloor.getX() - mSpeed);
    
    	}
    
    	private void drawFloor()
    	{
    		mFloor.draw(mCanvas, mPaint);
    	}
    
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh)
    	{
    		// 初始化地板
    		mFloor = new Floor(mWidth, mHeight, mFloorBg);
    
    	}
    
    }
    

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

    现在的效果:


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


    4、绘制管道

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

    package com.zhy.view;
    
    import java.util.Random;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.RectF;
    
    /**
     * 管道分为上下
     * 
     * @author zhy
     * 
     */
    public class Pipe
    {
    	/**
    	 * 上下管道间的距离
    	 */
    	private static final float RADIO_BETWEEN_UP_DOWN = 1 / 5F;
    	/**
    	 * 上管道的最大高度
    	 */
    	private static final float RADIO_MAX_HEIGHT = 2 / 5F;
    	/**
    	 * 上管道的最小高度
    	 */
    	private static final float RADIO_MIN_HEIGHT = 1 / 5F;
    	/**
    	 * 管道的横坐标
    	 */
    	private int x;
    	/**
    	 * 上管道的高度
    	 */
    	private int height;
    	/**
    	 * 上下管道间的距离
    	 */
    	private int margin;
    	/**
    	 * 上管道图片
    	 */
    	private Bitmap mTop;
    	/**
    	 * 下管道图片
    	 */
    	private Bitmap mBottom;
    
    	private static Random random = new Random();
    
    	public Pipe(Context context, int gameWidth, int gameHeight, Bitmap top,
    			Bitmap bottom)
    	{
    		margin = (int) (gameHeight * RADIO_BETWEEN_UP_DOWN);
    		// 默认从最左边出现
    		x = gameWidth;
    
    		mTop = top;
    		mBottom = bottom;
    
    		randomHeight(gameHeight);
    
    	}
    
    	/**
    	 * 随机生成一个高度
    	 */
    	private void randomHeight(int gameHeight)
    	{
    		height = random
    				.nextInt((int) (gameHeight * (RADIO_MAX_HEIGHT - RADIO_MIN_HEIGHT)));
    		height = (int) (height + gameHeight * RADIO_MIN_HEIGHT);
    	}
    
    	public void draw(Canvas mCanvas, RectF rect)
    	{
    		mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
    		// rect为整个管道,假设完整管道为100,需要绘制20,则向上偏移80
    		mCanvas.translate(x, -(rect.bottom - height));
    		mCanvas.drawBitmap(mTop, null, rect, null);
    		// 下管道,便宜量为,上管道高度+margin
    		mCanvas.translate(0, (rect.bottom - height) + height + margin);
    		mCanvas.drawBitmap(mBottom, null, rect, null);
    		mCanvas.restore();
    	}
    
    	public int getX()
    	{
    		return x;
    	}
    
    	public void setX(int x)
    	{
    		this.x = x;
    	}
    
    }
    

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

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

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

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

    筛检后的代码:

    public class GameFlabbyBird extends SurfaceView implements Callback, Runnable
    {
    
    	/**
    	 * *********管道相关**********************
    	 */
    	/**
    	 * 管道
    	 */
    	private Bitmap mPipeTop;
    	private Bitmap mPipeBottom;
    	private RectF mPipeRect;
    	private int mPipeWidth;
    	/**
    	 * 管道的宽度 60dp
    	 */
    	private static final int PIPE_WIDTH = 60;
    
    	private List<Pipe> mPipes = new ArrayList<Pipe>();
    
    	public GameFlabbyBird(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    		mPipeWidth = Util.dp2px(getContext(), PIPE_WIDTH);
    
    	}
    
    	/**
    	 * 初始化图片
    	 */
    	private void initBitmaps()
    	{
    		;
    		mPipeTop = loadImageByResId(R.drawable.g2);
    		mPipeBottom = loadImageByResId(R.drawable.g1);
    	}
    
    	private void draw()
    	{
    
    		drawBg();
    		drawBird();
    		drawPipes();
    		drawFloor();
    
    	}
    
    	/**
    	 * 绘制管道
    	 */
    	private void drawPipes()
    	{
    		for (Pipe pipe : mPipes)
    		{
    			pipe.setX(pipe.getX() - mSpeed);
    			pipe.draw(mCanvas, mPipeRect);
    		}
    	}
    
    	/**
    	 * 初始化尺寸相关
    	 */
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh)
    	{
    		super.onSizeChanged(w, h, oldw, oldh);
    
    		// 初始化管道范围
    		mPipeRect = new RectF(0, 0, mPipeWidth, mHeight);
    		Pipe pipe = new Pipe(getContext(), w, h, mPipeTop, mPipeBottom);
    		mPipes.add(pipe);
    
    	}
    
    }
    

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

    下面来看下效果:


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

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

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


    5、绘制分数

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

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

    筛检后的代码:

    public class GameFlabbyBird extends SurfaceView implements Callback,
    		Runnable
    {
    	/**
    	 * 分数
    	 */
    	private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1,
    			R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5,
    			R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 };
    	private Bitmap[] mNumBitmap;
    	private int mGrade = 100;
    	/**
    	 * 单个数字的高度的1/15
    	 */
    	private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f;
    	/**
    	 * 单个数字的宽度
    	 */
    	private int mSingleGradeWidth;
    	/**
    	 * 单个数字的高度
    	 */
    	private int mSingleGradeHeight;
    	/**
    	 * 单个数字的范围
    	 */
    	private RectF mSingleNumRectF;
    
    	/**
    	 * 初始化图片
    	 */
    	private void initBitmaps()
    	{
    
    		mNumBitmap = new Bitmap[mNums.length];
    		for (int i = 0; i < mNumBitmap.length; i++)
    		{
    			mNumBitmap[i] = loadImageByResId(mNums[i]);
    		}
    	}
    
    	private void draw()
    	{
    		// drawSomething..
    
    		drawBg();
    		drawBird();
    		drawPipes();
    		drawFloor();
    		drawGrades();
    
    	}
    	/**
    	 * 绘制分数
    	 */
    	private void drawGrades()
    	{
    		String grade = mGrade + "";
    		mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
    		mCanvas.translate(mWidth / 2 - grade.length() * mSingleGradeWidth / 2,
    				1f / 8 * mHeight);
    		// draw single num one by one
    		for (int i = 0; i < grade.length(); i++)
    		{
    			String numStr = grade.substring(i, i + 1);
    			int num = Integer.valueOf(numStr);
    			mCanvas.drawBitmap(mNumBitmap[num], null, mSingleNumRectF, null);
    			mCanvas.translate(mSingleGradeWidth, 0);
    		}
    		mCanvas.restore();
    
    	}
    
    
    	/**
    	 * 初始化尺寸相关
    	 */
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh)
    	{
    
    		super.onSizeChanged(w, h, oldw, oldh);
    		// 初始化分数
    		mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);
    		mSingleGradeWidth = (int) (mSingleGradeHeight * 1.0f
    				/ mNumBitmap[0].getHeight() * mNumBitmap[0].getWidth());
    		mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight);
    
    	}
    
    
    
    }
    

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

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

    现在的效果:


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

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



    源码点击下载



    建了一个QQ群,方便大家交流。群号:423372824

    ----------------------------------------------------------------------------------------------------------

    博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

    视频目录地址:本人录制的视频教程














    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    8.SpringMVC参数传递
    9.SpringMVC和json结合传递参数
    20160815命令行进入其他盘
    5.SpringMVC静态文件的访问
    6.SpringMVC注解启用
    7.SpringMVC注解优化
    3.SpringMVC修改配置文件路径和给界面传递数据
    rails
    SVN Merge合并 Patch打补丁
    Windows环境下怎么在文件夹下打开cmd命令行
  • 原文地址:https://www.cnblogs.com/dingxiaoyue/p/4924860.html
Copyright © 2020-2023  润新知