当前移动端开发有不少框架,如Cocos2d-x等,这些框架其实其原理仍是SurfaceView的操作。只不过是对其进行了优化和重用代码抽取。
所以,明确SurfaceView如何操作就能够更好的使用这些游戏引擎。
首先,建立一个GameView类,继承自SurfaceView:
SurfaceView的线程:
我们可以先看下SurfaceView的介绍:
// 1、Provides a dedicated drawing surface embedded inside of a view
// hierarchy.
// 2、One of the purposes of this class is to provide a surface in which a
// secondary thread can render in to the screen.
// If you are going to use it
// this way, you need to be aware of some threading semantics:
//
// ①All SurfaceView and SurfaceHolder.Callback methods will be called from
// the UI thread.
// ②You must ensure that the drawing thread only touches the underlying
// Surface while it is valid -- between
// SurfaceHolder.Callback.surfaceCreated() and
// SurfaceHolder.Callback.surfaceDestroyed().
在SurfaceView中有2种线程,1是UI线程,2是Render线程。和Activity不同,Activity的UIThread就是主线程,子线程无法操作主线程(即子线程无法更新UI),且无法在UIThread中进行耗时操作(否则报ANR(Application Not Responding错误),但是在SurfaceView中UIThread不负责界面的绘制,而是负责用户的操作,如划屏,点击等。而界面绘制是在RenderThread中完成。(RenderThread需要自定义)。
// UIThread : 大量的用户的操作
// secondary thread : 界面的绘制
SurfaceView的操作:
对SurfaveView的操作需要SurfaceHolder对象,如设置回调,获取画布(Canvas),画图等。在SurfaceView类中,可以通过getHolder()方法获取SurfaceView对象。
holder = getHolder();
SurfaceView的生命周期:
SurfaceView中有SurfaceHolder.CallBack()当SurfaceView状态变化会调用里面的方法。(即回调方法):
// The Callback is set with {@link SurfaceHolder#addCallback
// SurfaceHolder.addCallback} method.
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
通过SurfaceHolder.addCallBack()方法可以把该回调添加。
初始化RenderThread可以在SurfaceCreated中完成。线程销毁也可以在SurfaceDestory中完成。
如下:
public class RenderThread extends Thread{
public boolean isRender = true;
@Override
public void run() {
while (isRender ) {
//进行画图操作
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
renderThread = new RenderThread();
renderThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
renderThread.isRender = false;
renderThread.interrupt();
}
在SurfaceView中画图:
画图需要的首先就是画布(Canvas),没有画布,无法画图。
我们可以看下源码的类:
发现并没有能获取Canvas的方法,但是我们有SurfaceHolder,查看SurfaceHolder的源码发现有lockCanvas()方法能够获取Canvas,果断使用。
但,光是这样是不行的,需要操作完Canvas后调用unlockCanvasAndPost()方法。
看一下该方法的介绍:
* Finish editing pixels in the surface. After this call, the surface's
* current pixels will be shown on the screen, but its content is lost,
* in particular there is no guarantee that the content of the Surface
* will remain unchanged when lockCanvas() is called again.
即完成画图后调用,调用后会把画的图显示在屏幕上,但是不保存内容(Content is lost),当lockCanvas再次调用前,内容保持不变。
也就是说:
lockCanvas()和unlockCanvasAndPost()是一个周期。
每个周期会对SurfaceView的显示操作一次,如果想连续动作就需要多次lockCanvas和unlockCanvasAndPost()。
所以RenderThread中的操作如下:
public class RenderThread extends Thread{
public boolean isRender = true;
@Override
public void run() {
while (isRender ) {
//进行画图操作
Canvas canvas = holder.lockCanvas();
canvas.drawBitmap(bitmap, left, top, paint);
holder.unlockCanvasAndPost(canvas);
//此处可酌情添加Thread.sleep(),即睡眠时间。
}
}
}
由此可知,如果想让游戏更加流畅,需要在Canvas的画图上下功夫!!!