什么是SurfaceView?Surface的意思是表层,表面的意思,那么SurfaceView就是指一个在表层的View对象。为何 说是在表层呢,这是由于它有点特殊跟其余View不同,其余View是绘制在表层外,而它就是充当表层对象。假设你要在一个球上画画,那么球的表层就当 作你的画布对象,你画的东西会挡住它的表层,咱们默认没使用SurfaceView,那么球的表层就是空白的,若是咱们使用了SurfaceView,我 们能够理解为咱们拿来的球自己表面就具备纹路,你是画再纹路之上的,若是你画的是半透明的,那么你将能够透过你画的东西看到球面自己的纹路。SDK的文档 说到:SurfaceView就是在Window上挖一个洞,它就是显示在这个洞里,其余的View是显示在Window上,因此View能够显式在 SurfaceView之上,你也能够添加一些层在SurfaceView之上。php
SurfaceView还有其余的特性,上面咱们讲了它能够控制帧数,那它是什么控制的呢?这就须要了解它的使用机制。通常在不少游戏设计中,咱们都是开辟一个后台线程计算游戏相关的数据,而后根据这些计算完的新数据再刷新视图对象,因为对View执行绘制操做只能在UI线程上, 因此当你在另一个线程计算完数据后,你须要调用View.invalidate方法通知系统刷新View对象,因此游戏相关的数据也须要让UI线程能访 问到,这样的设计架构比较复杂,要是能让后台计算的线程能直接访问数据,而后更新View对象那改多好。咱们知道View的更新只能在UI线程中,因此使 用自定义View没办法这么作,可是SurfaceView就能够了。它一个很好用的地方就是容许其余线程(不是UI线程)绘制图形(使用Canvas),根据它这个特性,你就能够控制它的帧数,你若是让这个线程1秒执行50次绘制,那么最后显示的就是50帧。html
首先SurfaceView也是一个View,它也有本身的生命周期。由于它须要另一个线程来执行绘制操做,因此咱们能够在它生命周期的初始化阶 段开辟一个新线程,而后开始执行绘制,当生命周期的结束阶段咱们插入结束绘制线程的操做。这些是由其内部一个SurfaceHolder对象完成的。 SurfaceHolder,顾名思义,它里面保存了一个队Surface对象的引用,而咱们执行绘制方法就是操做这个 Surface,SurfaceHolder由于保存了对Surface的引用,因此使用它来处理Surface的生命周期,说到底 SurfaceView的生命周期其实就是Surface的生命周期,由于SurfaceHolder保存对Surface的引用,因此使用 SurfaceHolder来处理生命周期的初始化。android
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。编程
接下来呢,说说本身对它的理解
一、定义canvas
能够直接从内存或者DMA等硬件接口取得图像数据,是个很是重要的绘图容器。架构
它的特性是:能够在主线程以外的线程中向屏幕绘图上。这样能够避免画图任务繁重的时候形成主线程阻塞,从而提升了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽可能在画布canvas中画出。app
二、实现框架
首先继承SurfaceView并实现SurfaceHolder.Callback接口
使用接口的缘由:由于使用SurfaceView 有一个原则,全部的绘图工做必须得在Surface 被建立以后才能开始(Surface—表面,这个概念在 图形编程中经常被提到。基本上咱们能够把它看成显存的一个映射,写入到Surface 的内容
能够被直接复制到显存从而显示出来,这使得显示速度会很是快),而在Surface 被销毁以前必须结束。因此Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。ide
须要重写的方法函数
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在surface的大小发生改变时激发
(2)public void surfaceCreated(SurfaceHolder holder){}
//在建立时激发,通常在这里调用画图的线程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {}
//销毁时激发,通常在这里将画图的线程中止、释放。
整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()得到SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()得到Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
三、SurfaceHolder
这里用到了一个类SurfaceHolder,能够把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个须要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,通常在锁定后就能够经过其返回的画布对象Canvas,在其上面画图等操做了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..由于画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部份内存要求比较高的游戏来讲,能够不用重画dirty外的其它区域的像素,能够提升速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。
四、实例
这里的例子实现了一个矩形和一个计时器
package xl.test; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Bundle; import android.view.SurfaceHolder; import android.view.SurfaceView; public class ViewTest extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MyView(this)); } //视图内部类 class MyView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder holder; private MyThread myThread; public MyView(Context context) { super(context); // TODO Auto-generated constructor stub holder = this.getHolder(); holder.addCallback(this); myThread = new MyThread(holder);//建立一个绘图线程 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub myThread.isRun = true; myThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub myThread.isRun = false; } } //线程内部类 class MyThread extends Thread { private SurfaceHolder holder; public boolean isRun ; public MyThread(SurfaceHolder holder) { this.holder =holder; isRun = true; } @Override public void run() { int count = 0; while(isRun) { Canvas c = null; try { synchronized (holder) { c = holder.lockCanvas();//锁定画布,通常在锁定后就能够经过其返回的画布对象Canvas,在其上面画图等操做了。 c.drawColor(Color.BLACK);//设置画布背景颜色 Paint p = new Paint(); //建立画笔 p.setColor(Color.WHITE); Rect r = new Rect(100, 50, 300, 250); c.drawRect(r, p); c.drawText("这是第"+(count++)+"秒", 100, 310, p); Thread.sleep(1000);//睡眠时间为1秒 } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally { if(c!= null) { holder.unlockCanvasAndPost(c);//结束锁定画图,并提交改变。 } } } } } }
结来讲,就是SurfaceView有如下三个特色:
A. 具备独立的绘图表面;
B. 须要在宿主窗口上挖一个洞来显示本身;
C. 它的UI绘制能够在独立的线程中进行,这样就能够进行复杂的UI绘制,而且不会影响应用程序的主线程响应用户输入。
什么是双缓冲?
不用画布,直接在窗口上进行绘图叫作无缓冲绘图。用了一个画布,将全部内容都先画到画布上,在总体绘制到窗口上,就该叫作单缓冲绘图,那个画布就是一个缓冲区。用了两个画布,一个进行临时的绘图,一个进行最终的绘图,这样就叫作双缓冲绘图。
surfaceView自身实现了双缓冲,而View没有。其实view你也能够本身实现,可是实现的结构不如surfaceView好。
surfaceView经过 surfaceHolder.lockCanvas 锁定画布,实现下一张图片的绘制,再经过另外的线程刷新界面,绘制图片。
view则是直接在ondraw里绘制图片,刷新界面。其实view也能够实现双缓冲机制,你能够在另个出ondraw的方法中绘制下一张bitmap(参见:http://blog.csdn.net/liubingzhao/article/details/5563113),也能够另开一个线程,处理除了绘制图片之外的操做(参见:http://topic.csdn.net/u/20110901/23/e283f805-20dc-40c3-8381-403dd1ca69b0.html),就实现了view的双缓冲。
为何动态绘图surfaceView要比View好?
由于View是在UI主线程中进行绘制的,绘制时会阻塞主线程,若是ontouch事件又处理的比较多的话会致使界面卡。而surfaceView是另开了一个线程绘制的,再加上双缓冲机制,因此要高效。不会卡。其实如今通常实现view的时候通常都会在其余出先生成bitmap在给ondraw去画,因此双缓冲的做用不是那么明显了。