Android 画图类View与SurfaceView之学习

在开发游戏开发中,android相应的提供了几个重要的模块:

1、显示界面的视图:  Android 提供 View 和 SurfaceView 

2、控制游戏整体结构: android 提供 Activity 

3、逻辑控制类:专门用于处理游戏的逻辑计算

4、处理游戏界面与用户交互事件 : 利用 View 类提供的 onKeyDown onKeyUp onTounchEvent等方法


我们这里简单熟悉一下如何在视图上画东西。


1、View 类: android.view.View 

View 是Android中的一个超类,这个类几乎包含了所有的屏幕类型。每一个View都有一个用于绘画的画布,这个画布 可以进行任何扩展。

任何一个View类都只需要重写onDraw方法来实现界面显示,任何一个View都只需要重写 OnDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本或位图。


Android 中有个重要的东东: Android UI 线程 

在这里 http://blog.csdn.net/andyhuabing/article/details/11921887 有对其简要精典的介绍及注意点,这里就不再重复说明了。


这里来个简单例子说明一下View的用法,利用线程变更显示颜色,通过上下左右移动矩形


TestView.java 类如下:

package com.example.testondraw;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

/**
 * View 是Android中的一个超类,这个类几乎包含了所有的屏幕类型。每一个View都有一个用于绘画的画布,这个画布 可以进行任何扩展。
 * 
 * 任何一个View类都只需要重写onDraw方法来实现界面显示。
 * 
 */
public class TestView extends View {
	int miCount = 0;
	int x = 10, y = 10;

	public TestView(Context context) {
		super(context);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (miCount < 100)
		{
			miCount++;
		}
		else 
		{
			miCount = 0;
		}
		//绘图
		Paint mPaint = new Paint();   
        switch (miCount%4)
		{
		case 0:
			mPaint.setColor(Color.BLUE);  	
			break;
		case 1:
			mPaint.setColor(Color.GREEN);  	
			break;
		case 2:
			mPaint.setColor(Color.RED);
			break;
		case 3:
			mPaint.setColor(Color.YELLOW);
			break;
		default:
			mPaint.setColor(Color.WHITE);
			break;
		}
		// 绘制矩形
		canvas.drawRect(x, y, x + 120, y + 80, mPaint);
	}
}


如何在 Acitivty 使用呢?示例代码如下:

package com.example.testondraw;

import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;

public class MainActivity extends Activity {
	static final String TAG = "MainActivity";
	private TestView mTestView = null;
	private TestSurfaceView mTestSurfaceView = null;
	private int mWidth = 0, mHeight = 0;
	private MyHandler mHandler = new MyHandler();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		initView();

		startTestView();
		
		// setContentView(R.layout.main);
	}

	void initView() {
		// 使用自定义的View
		mTestView = new TestView(this);
		setContentView(mTestView);
	
		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		mWidth = dm.widthPixels;
		mHeight = dm.heightPixels;
		Log.i(TAG, "Display Metrics " + mWidth + " mHeight:" + mHeight);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	void startTestView() {
		Thread th = new Thread(new Runnable() {

			@Override
			public void run() {
				while (!Thread.currentThread().isInterrupted()) {
					// 使用postInvalidate可以直接在线程中更新界面
//					mTestView.postInvalidate();
					
					// 使用发消息给UI线程
					Message msg = Message.obtain();
					msg.what = 1;
					mHandler.sendMessage(msg);

					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						Thread.currentThread().interrupt();
					}
				}
			}
		});
		th.start();
	}

	class MyHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				mTestView.invalidate();
				break;
			default:
				break;
			}
			super.handleMessage(msg);
		}
	}

	public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_DPAD_DOWN:
			if (mTestView.y >= mHeight)
				mTestView.y = 0;
			mTestView.y += 10;
			break;
		case KeyEvent.KEYCODE_DPAD_UP:
			if (mTestView.y <= 0)
				mTestView.y = mHeight;
			mTestView.y -= 10;
			break;
		case KeyEvent.KEYCODE_DPAD_LEFT:
			if (mTestView.x <= 0)
				mTestView.x = mWidth;
			mTestView.x -= 10;
			break;
		case KeyEvent.KEYCODE_DPAD_RIGHT:
			if (mTestView.x >= mWidth)
				mTestView.x = 0;
			mTestView.x += 10;
			break;
		}
		return false;
	};
}


2、SurfaceView  android.view.SurfaceView

当执行效率有要求很高时,View类就无法满足需求。必须使用 SurfaceView 类 -- 利用双缓冲技术


使用SurfaceView提供给需要直接画像素而不是使用画窗体部件的应用使用。

而每个Surface创建一个Canvas对象,用来管理View在Surface上的绘画操作。


简要说明一下具体的方法及使用:

SurfaceHolder 对象需要通过 getHolder() 获取

在 Layout 上摆一个 SurfaceView 组件:

mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
SurfaceHolder holder = mSurfaceView.getHolder();


对于SurfaceView 的创建,销毁及变更

@SuppressWarnings("unused")
private SurfaceHolder.Callback mSurfaceCbk = new SurfaceHolder.Callback() {


@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {    // 在Surface 大小发生变更时触发
// TODO Auto-generated method stub

}


@Override
public void surfaceCreated(SurfaceHolder holder) { // 在创建 Surface 时触发
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) { // 在销毁 Surface 时触发
// TODO Auto-generated method stub

}

};


添加及删除 SurfaceView 的回调函数:

display.addCallback(mSurfaceCbk);

display.removeCallback(mSurfaceCbk);


Canvas 的使用:

lockCanvas  锁定画布,绘图之前必须锁定画布才能得到当前画布对象

unlockCanvasAndPost 开始绘制时锁定画布,绘制完成之后解锁画布


下面例子是绘制一个不断变换颜色的圆,并实现 SurfaceView 的事件处理

TestSurfaceView.java 代码:

package com.example.testondraw;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * 使用SurfaceView提供给需要直接画像素而不是使用画窗体部件的应用使用。
 * 而每个Surface创建一个Canvas对象,用来管理View在Surface上的绘画操作。
 */
public class TestSurfaceView extends SurfaceView implements
		SurfaceHolder.Callback {
	// 控制循环
	private boolean mbLoop = false;

	// 定义SurfaceHolder对象
	private SurfaceHolder mSurfaceHolder = null;
	private int miCount = 0;
	public int x = 50, y = 50;
	private int mWidth = 1280,mHeight = 720;
	private Bitmap mBitmap = null;
	
	public TestSurfaceView(Context context) {
		super(context);

		// 实例化SurfaceHolder
		mSurfaceHolder = this.getHolder();

		// 添加回调
		mSurfaceHolder.addCallback(this);
		this.setFocusable(true);
	}

	public void setDisplayWH(int w, int h) {
		mWidth = w;
		mHeight = h;
	}

	public void setBitmap(Bitmap bitmap) {
		this.mBitmap = bitmap;
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		mbLoop = true;

		Thread th = new Thread(new Runnable() {

			@Override
			public void run() {
				while (mbLoop){
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					synchronized( mSurfaceHolder ){
						drawBitmap();
						DrawData();
					}
				}
			}
		});
		th.start();
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		mbLoop = false;
	}

	private void drawBitmap() {
		// 锁定画布,得到canvas
		if (mSurfaceHolder == null || this.mBitmap == null)
			return;

		Canvas canvas = mSurfaceHolder.lockCanvas();
		if (canvas == null) {
			return;
		}

		// 绘图
		Paint paint = new Paint();
		paint.setAntiAlias(true);
		paint.setColor(Color.BLUE);

		canvas.drawBitmap(this.mBitmap, 0, 0, paint);

		// 绘制后解锁,绘制后必须解锁才能显示
		mSurfaceHolder.unlockCanvasAndPost(canvas);
	}

	// 绘图方法
	private void DrawData() {
		if (mSurfaceHolder == null)
			return;

		// 锁定画布,得到canvas
		Canvas canvas = mSurfaceHolder.lockCanvas();
		if (canvas == null) {
			return;
		}

		if (miCount < 100) {
			miCount++;
		} else {
			miCount = 0;
		}

		// 绘图
		Paint mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.BLACK);

		// 绘制矩形--清屏作用
		canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
		
		switch (miCount % 4) {
		case 0:
			mPaint.setColor(Color.BLUE);
			break;
		case 1:
			mPaint.setColor(Color.GREEN);
			break;
		case 2:
			mPaint.setColor(Color.RED);
			break;
		case 3:
			mPaint.setColor(Color.YELLOW);
			break;
		default:
			mPaint.setColor(Color.WHITE);
			break;
		}

		// 绘制矩形--
		canvas.drawCircle(x, y, 50, mPaint);
		
		// 绘制后解锁,绘制后必须解锁才能显示
		mSurfaceHolder.unlockCanvasAndPost(canvas);
	}
}


测试使用例子:


package com.example.testondraw;

import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity {
	static final String TAG = "MainActivity";
	private TestSurfaceView mTestSurfaceView = null;
	private int mWidth = 0, mHeight = 0;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		initView();

		startTestSurfaceView();
		
		// setContentView(R.layout.main);
	}

	void initView() {
		// 使用自定义的View
		mTestSurfaceView = new TestSurfaceView(this);
		setContentView(mTestSurfaceView);		

		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		mWidth = dm.widthPixels;
		mHeight = dm.heightPixels;
		Log.i(TAG, "Display Metrics " + mWidth + " mHeight:" + mHeight);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_DPAD_DOWN:
			if (mTestSurfaceView.y >= mHeight)
				mTestSurfaceView.y = 0;
			mTestSurfaceView.y += 10;
			break;
		case KeyEvent.KEYCODE_DPAD_UP:
			if (mTestSurfaceView.y <= 0)
				mTestSurfaceView.y = mHeight;
			mTestSurfaceView.y -= 10;
			break;
		case KeyEvent.KEYCODE_DPAD_LEFT:
			if (mTestSurfaceView.x <= 0)
				mTestSurfaceView.x = mWidth;
			mTestSurfaceView.x -= 10;
			break;
		case KeyEvent.KEYCODE_DPAD_RIGHT:
			if (mTestSurfaceView.x >= mWidth)
				mTestSurfaceView.x = 0;
			mTestSurfaceView.x += 10;
			break;
		case KeyEvent.KEYCODE_BACK:
			this.finish();
			break;
		}
		return false;
	};

	void startTestSurfaceView() {
		mTestSurfaceView.setDisplayWH(mWidth, mHeight);
        }
}


原文地址:https://www.cnblogs.com/keanuyaoo/p/3365964.html