1 介绍
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
Surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。Surfaceview提供了一个可见区域,只有在这个可见区域内Surface部分内容才可见,可见区域外的部分不可见。
Surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface 的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。如果 surface 上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件之间的透明效果,这会影响性能。
你可以通过getHolder()返回的SurfaceHolder实例访问Surface。Surfaceview变得可见时,Surface被创建;Surfaceview隐藏前,Surface被销毁,这样能节省资源。如果你要查看Surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
这里应注意:
- 所有SurfaceView和SurfaceHolder.Callback的方法都会在UI线程里调用,一般来说就是应用程序主线程。所以渲染线程所要访问的各种变量应该作同步处理。
- 由于Surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的Surface。
SurfaceView最终会和UI线程打交道:
SurfaceView带有独立的Surface(独立与Window的Surface),这可以让子线程在独立的Surface上面绘制东西,进行SurfaceView的界面绘制,这个子线程就叫做渲染线程,但是要让独立的Surface上面的东西在View上面展示出来,需要post一个消息给主线程,目的是把该Surface中canvas 上的东西绘制到View的真正的画布上面(window的Surface的canvas上)。
2 SurfaceView和View的不同之处
| View | SurfaceView |
|---|---|
| 适用于主动更新 | 适用于被动刷新 |
| 在主线程中进行画面更新 | 通常通过一个子线程来进行画面更新 |
| 绘图中没有使用双缓冲机制 | 在底层实现中就实现了双缓冲机制 |
| 比较了上面的不同之处,显然可以发现,如果一个View需要频繁的刷新,或者在刷新时数据处理量大(可能引起卡顿),可以考虑使用SurfaceView来替代View。 |
2.1 SurfaceView的双缓冲机制
对于每一个SurfaceView对象而言,有两个独立的graphic buffer。在Android SurfaceView的双缓冲机制中是这样实现的:
在Buffer A中绘制内容,然后让屏幕显示Buffer A;在下一个循环中,在Buffer B中绘制内容,然后让屏幕显示Buffer B,如此往复。而由于这个双缓冲机制的存在,可能会引起闪屏现象。在第一个”lockCanvas-drawCanvas-unlockCanvasAndPost “循环中,更新的是buffer A的内容;到下一个”lockCanvas-drawCanvas-unlockCanvasAndPost”循环中,更新的是buffer B的内容。 如果buffer A与buffer B中某个buffer内容为空,当屏幕轮流显示它们时,就会出现画面黑屏闪烁现象。
解决方法:
当准备更新内容时,先判断内容是否为空,只有非空时才启动”lockCanvas-drawCanvas-unlockCanvasAndPost”这个流程。
3 SurfaceView使用步骤
- 获取到SurfaceView对应的SurfaceHolder,通过addCallback()方法设置SurfaceHolder.Callback用于监听Surface的创建和销毁。
- 创建渲染线程对象。
- 在子线程中使用SurfaceHolder的lockCanvas获取Surface上面指定区域的Canvas。
- 在该Canvas上绘制图形。绘制结束后,使用SurfaceHolder的unlockCanvasAndPost()方法解锁Canvas,并且让UI线程把Surface上面的东西绘制到View的Canvas上面。
3.1 使用SurfaceHolder的例子
1 | public class GameUI extends SurfaceView implements SurfaceHolder.Callback { |
需要注意的坑:
1、某些手机按HOME键回到桌面后再从后台切回APP前台时,并没有重新走surfaceCreated、surfaceChanged方法。(按HOME键回到桌面会调用surfaceDestroyed方法。)
2、对于上面第一点,我在SurfaceView中使用camera1又不会出现问题,但类似上文中的例子会出现第一点所说的问题。