Android 游戏开发框架核心组件
核心组件介绍
SurfaceView 介绍
SurfaceView 介绍
- SurfaceView 就是带 Surface 的 view,它是一个 View,是 View 的子类,所以和其他 View 一样,可以在屏幕上展示东西接收用户输入,具有 View 的生命周期回调函数,如 onMeasure、onLayout、onDraw、onTouchEvent 等
- SurfaceView 带有独立的 Surface(独立与 window 的 surface),这可以让子线程在独立的 Surface 上面绘制东西,进行 SurfaceView 的界面绘制,这个子线程就叫做渲染线程,但是要让独立的 Surface 上面的东西在 View 上面展示出来,需要 post 一个消息给主线程,目的是把该 Surface 中 canvas 上的东西绘制到 View 的真正的画布上面(window 的 surface 的 canvas上),这样就可以把 UI 线程空闲出来处理用户的交互
- Surface 可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed() 之间有效,这只是说 Surface 创建和销毁的时候会回到前面两个方法,所以要确保渲染线程访问的是合法有效的 surface
- SurfaceHolder.CallBack 是通过 SurfaceView 的 SurfaceHolder 的 addCallback 来设置给 SurfaceHolder 的,让 SurfaceView 实现 CallBack 并设置给 SurfaceHolder,SurfaceView 就可以监听这个独立 Surface 的创建和销毁了。
sdk 中的介绍
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 surface 部分内容才可见,可见区域外的部分不可见。
surface 的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface
的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果 surface 上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件之间的透明效果,这会影响性能。
你可以通过 surfaceHolder 接口访问这个surface,getHolder() 方法可以得到这个接口。
surfaceview 变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface 被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder) surfaceView 的核心在于提供了两个线程:UI线程和渲染线程。
这里应注意:
1. 所有 SurfaceView 和 SurfaceHolder.Callback 的方法都会在UI线程里调用,一般来说就是应用程序主线程。所以渲染线程所要访问的各种变量应该作同步处理。
2. 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的
surface。
SurfaceHolder 介绍
SurfaceHolder 是对 SurfaceView 的 Surface 的包装,不但在 SurfaceHolder.callback 接口中负责 Surface 创建和销毁的回调,而且还对 Surface 的关键方法 LockCanvas()、unLockCanvasAndPost() 方法进行了线程安全的包装,所以 SurfaceHolder 是 Surface 对象的持有者,负责 Surface 的生命周期中的对 Surface 操作的方法的调用
脏矩形 Rect dirty,是指标记这块矩形区域的数据作废,也就是需要重写绘制的矩形区域,LockCanvas(Rect dirty),可以指定一个矩形区域,让 Surface 中的 Canvas 上部分数据重绘。
SurfaceView、SurfaceHolder、Surface 之间的关系
SurfaceView 使用的步骤
- 获取到 SurfaceView 对应的 SurfaceHolder,给 SurfaceHolder 添加一个 SurfaceHolder.callback 对象。
- 创建渲染线程对象
- 在子线程中开始在 Surface 上面绘制图形,因为SurfaceView没有对我们暴露 Surface,而只是暴露了 Surface 的包装器 SurfaceHolder,所以使用 SurfaceHolder 的 lockCanvas()获取 Surface 上面指定区域的 Canvas,在该 Canvas 上绘制图形,绘制结束后,使用 SurfaceHolder 的 unlockCanvasAndPost()方法解锁 Canvas,并且让 UI 线程把 Surface 上面的东西绘制到 View 的 Canvas 上面
SurfaceView 使用的 demo
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">GameUI</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">SurfaceView</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">SurfaceHolder</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Callback</span> {</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> SurfaceHolder holder;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> RenderThread renderThread;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isDraw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 控制绘制的开关</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">GameUI</span>(Context context) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(context);
holder = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.getHolder();
holder.addCallback(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
renderThread = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RenderThread();
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">surfaceChanged</span>(SurfaceHolder holder, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> format, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> width, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height) {
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">surfaceCreated</span>(SurfaceHolder holder) {
isDraw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
renderThread.start();
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">surfaceDestroyed</span>(SurfaceHolder holder) {
isDraw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 绘制界面的线程
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @author</span> Administrator
*
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RenderThread</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Thread</span> {</span>
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 不停绘制界面</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (isDraw) {
drawUI();
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.run();
}
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 界面绘制
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">drawUI</span>() {
Canvas canvas = holder.lockCanvas();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
drawCanvas(canvas);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
e.printStackTrace();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> {
holder.unlockCanvasAndPost(canvas);
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">drawCanvas</span>(Canvas canvas) {
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 在 canvas 上绘制需要的图形</span>
}
}</code>