SurfaceView你所应该知道的事

来源:互联网 发布:连通区域图像分割算法 编辑:程序博客网 时间:2024/05/18 01:28

转载注明出处:http://blog.csdn.net/pangrui201/article/details/50834765
我们知道android对UI的绘制是在主线程里完成的,可是对一些交互性比较强比如拍照功能或者游戏开发等需要及时响应用户输入的需求,显然在在主线程里做UI绘制是不理想的,一方面会导致复杂的UI绘制卡顿或者用户输入不能及时响应影响用户体验,另一方面很容易造成主线程堵塞发生ANR。
SurfaceView内部实现双缓冲机制很好的解决这个问题,具体是使用主线程来负责UI的显示和渲染线程做UI的绘制,两个线程交替进行,这样两个线程显示界面的效率非常快,当然对内存和cpu的开销也是非常大的。

线程的简单示意如下

A   加载数据  显示界面B   显示界面  加载数据

extends View
java.lang.Object
android.view.View
android.view.SurfaceView

从继承关系可以看出SurfaceView的依旧是View的子类,下面我们看看Google官方文档关于SurfaceView是怎么描述的,其中重要的语句用红色标出。

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen
The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.
The transparent region that makes the surface visible is based on the layout positions in the view hierarchy. If the post-layout transform properties are used to draw a sibling view on top of the SurfaceView, the view may not be properly composited with the surface.
Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().
The Surface will be created for you while the SurfaceView’s window is visible; you should implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.
One of the purposes of this class is to provide a surface in which a secondary thread can render into 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 thread running the SurfaceView’s window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing 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是视图(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。

通过SurfaceView的API了解我们可以发现:

核心的类

1 SurfaceView 展示的屏幕

2 SurfaceHolder 展示的内容

3 Thread 绘制任务(渲染线程)

需要满足两个条件 1. 在surfaceCreated之后创建, 在surfaceDestroyed 销毁

总结

1.这里的SurfaceHolder可以通过getHolder获取,surfaceCreated和在surfaceDestroyed可以通过添加实现 SurfaceHolder.Callback回调得到,不过这里必须注意的是在构造方法里面需要添加holder.addCallback(this);这样才能注册了 SurfaceHolder.Callback,这样surfaceCreated和surfaceDestroyed这两个方法才会有效。

2.这里的渲染线程必须在surfaceCreated和surfaceDestroyed之间
执行绘制任务.

3.为了保证线程的安全性一般在绘制前锁定画布Canvas lockCanvas = holder.lockCanvas();绘制完成后解锁画布并提交holder.unlockCanvasAndPost(lockCanvas);// 解锁画布并提交 如果不执行的话上面的代码都没有效果。

4.由于绘图渲染是具有图层的,所以通常会在渲染线程的run方法里面while(true)来绘图。

根据以上几点的总结典型的SurfaceView代码模板如下

  //想让 SurfaceHolder.Callback 生效 必须 SurfaceHolder.addCallBack(SurfaceHolder.Callback)    public class UIView extends SurfaceView implements SurfaceHolder.Callback {private RenderThread renderThread;private boolean flag;// 线程运行的标记private SurfaceHolder holder;public UIView(Context context) {super(context);holder = getHolder();holder.addCallback(this); // 保证surfaceCreated,surfaceChanged,surfaceDestroyed// 生效}private class RenderThread extends Thread {@Overridepublic void run() {while (flag) {try {long startTime = System.currentTimeMillis();drawUI();long endTime=System.currentTimeMillis();long dTime=endTime-startTime;  //绘制一次的时间int fps=(int) (1000/dTime);  // 1秒中可以绘制的次数 帧数  >30  System.out.println(fps);} catch (Exception e) {e.printStackTrace();}}}}// 绘制public void drawUI() {// 锁定画布Canvas lockCanvas = holder.lockCanvas();// // 绘制界面Paint paint = new Paint();// 画笔        //TODO        //执行画图渲染        //......// 解锁画布并提交holder.unlockCanvasAndPost(lockCanvas);// 解锁画布并提交 如果不执行的话 上面的代码 都没有效果}// 当Surface创建的时候调用@Overridepublic void surfaceCreated(SurfaceHolder holder) {      Log.d("TAG", "surfaceCreated");renderThread = new RenderThread();flag = true;renderThread.start();// 开启线程了...}// 当Surface销毁的时候调用@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {         Log.d("TAG", "surfaceDestroyed");// 一般停止线程 都是控制线程的循环flag = false;}// 当Surface改变时候调用@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {          Log.d("TAG", "surfaceChanged");}/** * 处理屏幕的点击事件 *  * @param event */public void handleTouch(MotionEvent event) {switch (event.getAction()) {//case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_DOWN:  // 当按下的时候break;case MotionEvent.ACTION_UP:default:break;}}

核心分析就到这里,下面通过以上分析过程用SurfaceView来实现一个hello显示的demo,其他在复杂的UI绘制也可以直接套用该模板,实现自己的drawUI即可。代码注释很详细,这里不再累述。有兴趣的可以下载研究,代码下载路径。
点击下载源码http://download.csdn.net/detail/pangrui201/9458648

1 0
原创粉丝点击