Android开发之SurfaceView
SurfaceView介绍
通常情况程序的View和用户响应都是在同一个线程中处理的,这也是为什么处理长时间事件(例如访问网络)需要放到另外的线程中去(防止阻塞当前UI线程的操作和绘制)。但是在其他线程中却不能修改UI元素,例如用后台线程更新自定义View(调用View的在自定义View中的onDraw函数)是不允许的。
如果需要在另外的线程绘制界面、需要迅速的更新界面或则渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了。SurfaceView中包含一个Surface对象,而Surface是可以在后台线程中绘制的。Surface属于OPhone底层显示系统,SurfaceView的性质决定了其比较适合一些场景:需要界面迅速更新、对帧率要求较高的情况。使用SurfaceView需要注意以下几点情况:
- SurfaceView和SurfaceHolder.Callback函数都从当前SurfaceView窗口线程中调用(一般而言就是程序的主线程)。有关资源状态要注意和绘制线程之间的同步。
- 在绘制线程中必须先合法的获取Surface才能开始绘制内容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之间的状态为合法的,另外在Surface类型为SURFACE_TYPE_PUSH_BUFFERS时候是不合法的。
- 额外的绘制线程会消耗系统的资源,在使用SurfaceView的时候要注意这点。
使用SurfaceView
只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知View,SurfaceHolder.Callback具有如下的接口:
- surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。
- surfaceChanged(SurfaceHolder holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。
- surfaceDestroyed(SurfaceHolder holder):当Surface被摧毁前会调用该函数,该函数被调用后就不能继续使用Surface了,一般在该函数中来清理使用的资源。
通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。
当在Canvas中绘制完成后,调用函数unlockCanvasAndPost(Canvas canvas)来通知系统Surface已经绘制完成,这样系统会把绘制完的内容显示出来。为了充分利用不同平台的资源,发挥平台的最优效果可以通过SurfaceHolder的setType函数来设置绘制的类型,目前接收如下的参数:
- SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
- SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
- SURFACE_TYPE_GPU:适用于GPU加速的Surface
- SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。
使用的SurfaceView的时候,一般情况下还要对其进行创建,销毁,改变时的情况进行监视,这就要用到SurfaceHolder.Callback。
访问SurfaceView的底层图形是通过SurfaceHolder接口来实现的,通过getHolder()方法可以得到这个SurfaceHolder对象。你应该实现surfaceCreated(SurfaceHolder)和surfaceDestroyed(SurfaceHolder)方法来知道在这个Surface在窗口的显示和隐藏过程中是什么时候创建和销毁的。
注意:一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的(null)。
SurfaceView和View最本质的区别
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类。
1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
Android开发之SurfaceView的更多相关文章
- Android开发之Java集合类性能分析
对于Android开发者来说深入了解Java的集合类很有必要主要是从Collection和Map接口衍生出来的,目前主要提供了List.Set和 Map这三大类的集合,今天Android吧(ard8. ...
- Android开发之InstanceState详解
Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...
- Android开发之Git配置
Android开发之Git配置 1.首先git配置: 输入命令: git config --global user.name "xxx.xx" git config --globa ...
- 【Android UI】Android开发之View的几种布局方式及实践
引言 通过前面两篇: Android 开发之旅:又见Hello World! Android 开发之旅:深入分析布局文件&又是“Hello World!” 我们对Android应用程序运行原理 ...
- Android开发之旅: Intents和Intent Filters(理论部分)
引言 大部分移动设备平台上的应用程序都运行在他们自己的沙盒中.他们彼此之间互相隔离,并且严格限制应用程序与硬件和原始组件之间的交互. 我们知道交流是多么的重要,作为一个孤岛没有交流的东西,一定毫无意义 ...
- Android开发之ViewPager+ActionBar+Fragment实现响应式可滑动Tab
今天我们要实现的这个效果呢,在Android的应用中十分地常见,我们可以看到下面两张图,无论是系统内置的联系人应用,还是AnyView的阅读器应用,我们总能找到这样的影子,当我们滑动屏幕时,Tab可 ...
- Android开发之Java必备基础
Android开发之Java必备基础 Java类型系统 Java语言基础数据类型有两种:对象和基本类型(Primitives).Java通过强制使用静态类型来确保类型安全,要求每个变量在使用之前必须先 ...
- Android开发之PopupWindow
/* * Android开发之PopupWindow * * Created on: 2011-8-8 * Author: blueeagle * Email: liujiaxiang@g ...
- [置顶] Android开发之MediaPlayerService服务详解(一)
前面一节我们分析了Binder通信相关的两个重要类:ProcessState 和 IPCThreadState.ProcessState负责打开Binder 驱动,每个进程只有一个.而 IPCThre ...
随机推荐
- 在 vmware player中安装 ubuntu 17.10
目录 在 vmware 中安装 ubuntu 17.10 分区参考 vmware安装助手 第一步:更新 安装vmware tools 调整显示 第二步.输入法 五笔输入法 搜狗拼音输入法 ibus不能 ...
- 黄聪:初识Pjax:pjax是什么
听说博主不再折腾wordpress了,陌小雨还是转载到网站做个备份吧,万一哪天没有了呢.陌小雨觉得讲的挺清楚的,小白都能懂. pjax是 pushstate + ajax,分别百度可以得到相关资料,在 ...
- Java 内存溢出(java.lang.OutOfMemoryError)情况总结
最近做一个项目,因为分了十几个模块,但是每次在Eclipse中启动Tomcat必须加载四五个模块,这样出现了 java.lang.OutOfMemoryError 原因是Eclipse中Tomcat设 ...
- vc++获取网页源码之使用类型库(TypeLib)生成包装类
1.在MFC项目名称上 右击->添加->选择Visual C++下的MFC->TypeLib中的MFC类->添加 可以从注册表表中共或是文件中根据相应的接口生成对应的包装类 效 ...
- Java学习——方法中传递参数分简单类型与复杂类型(引用类型)编程计算100+98+96+。。。+4+2+1的值,用递归方法实现
package hello; public class digui { public static void main(String[] args) { // TODO Auto-generated ...
- Centos7.3安装部署Zabbix3.4.15(成功可用)
1.Xshell 远程连接到Centos7.3.连接centos 系统后,首先关闭防火墙和SELINUX,如不关闭会各种拦截,网页访问等故障,容易造成蛋疼哦.#systemctl stop firew ...
- SCCM2012 R2实战系列之十一:解决OSD分发Windows7 系统盘盘符为’D’问题
在SCCM 2012 R2操作系统分发(OSD)中,大家最早做的实验可能就是分发干净的Windows7和Windows 8(或Windows8.1)了吧.但是不可面对的问题就是相同配置的任务序列(只是 ...
- Windows Server 2012 R2 无法启用Microsoft .NET Framework 3.5 功能
1 在新windows 2012 R2 上安装SQL 2014 ,提示需要安装 .NET Framework 3.5 2 在添加角色和功能--功能--.NET Framework 3.5,然后失败 3 ...
- delphi EncdDecd.pas单元中Encoding方法出现#$D#$A的解决方法
例如: s:= 'http://detail.tmall.com/item.htm?id=45545826531&abbucket=_AB-M129_B17&acm=03130.100 ...
- Chapter3:Qt5布局管理
3.1分割窗口QSplitter类 QSplitter *splitterMain = new QSplitter(Qt::Horizontal,0); QTextEdit *textLeft = n ...