SurfaceView使用介绍
.
介绍
SurfaceView 提供嵌入视图层次结构内部的专用绘图表面。您可以控制此表面的格式,也可以控制其大小;SurfaceView负责将表面放置在屏幕上的正确位置。
补充:
SDK的文档说到:SurfaceView就是在Window上挖一个洞,它就是显示在这个洞里,其他的View是显示在Window上,所以View可以显式在 SurfaceView之上,你也可以添加一些层在SurfaceView之上。
.
使用场景
如果需要在另外的线程绘制界面、需要迅速的更新界面或则渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了。SurfaceView中包含一个Surface对象,而Surface是可以在后台线程中绘制的。Surface属于OPhone底层显示系统,SurfaceView的性质决定了其比较适合一些场景:需要界面迅速更新、对帧率要求较高的情况;例如:视频播放,游戏,录像等等。
.
SurfaceView的常用方法
继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了
SurfaceHolder.Callback
的作用:在底层的Surface状态发生变化的时候通知View
SurfaceHolder.Callback里的方法:
surfaceCreated()方法:
说明: 当Surface第一次创建后会立即调用该函数。
作用: 程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制SurfacesurfaceChanged()方法:
说明: 当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次surfaceDestroyed()方法:
说明: 当Surface被摧毁前会调用该函数,该函数被调用后就不能继续使用Surface了
作用: 一般在该函数中来清理使用的资源unlockCanvasAndPost()方法:
作用: 系统Surface已经绘制完成,这样系统会把绘制完的内容显示出来
SurfaceView 和 View 的区别?
-
View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新
-
View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿
-
SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面(如视频播放界面)
-
SurfaceView执行动画的效率比View高,而且你可以控制帧数
.
.
自定义SurfaceView的使用介绍
1. 创建 MySurfaceView
类并继承 SurfaceView
public class MySurfaceView extends SurfaceView implements Callback{
private DrawThread thread;
//构造方法
public MySurfaceView(Context context) {
super(context);
//初始化,设置生命周期回调方法
init();
}
private void init(){
//获取SurfaceHolder对象
SurfaceHolder holder = getHolder();
//设置Surface生命周期回调
holder.addCallback(this);
//初始化自定义的绘制线程
thread = new DrawThread(holder, getContext());
}
@Override
//Surface的状态发生变化的时候会调用该函数
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
//第一次创建Surface后会立即调用该函数
public void surfaceCreated(SurfaceHolder holder) {
thread.isRunning = true;//设置绘制线程的状态
thread.start();//启动子线程
}
@Override
//Surface被摧毁前会调用该函数
public void surfaceDestroyed(SurfaceHolder holder) {
//销毁绘制线程
thread.isRunning = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 创建一个线程执行绘制的操作
class DrawThread extends Thread{
private SurfaceHolder surfaceHolder;//定义SurfaceHolder对象
private Context context;//定义上下文对象
private Canvas c = null;//定义画布对象
private Paint paint;//定义画笔对象
public DrawThread(SurfaceHolder surfaceHolder,Context context){
this.surfaceHolder = surfaceHolder;
this.context = context;
//初始化画笔对象
paint = new Paint();
//设置画笔的属性和样式
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.STROKE);
}
@Override
public void run() {
try{
synchronized (surfaceHolder) {
//从surfaceHolder中获取画布对象,并上锁
c = surfaceHolder.lockCanvas(null);
//开始绘制图像
doDraw(c);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解除画布的锁定
surfaceHolder.unlockCanvasAndPost(c);
}
}
//绘制图像
public void doDraw(Canvas c){
//这个很重要,清屏操作,清楚掉上次绘制的残留图像
c.drawColor(Color.BLACK);
/*---使用Canvas画布对象来绘制图像---*/
}
}
3. 在 Activity_surface_view
的xml文件中引入自定义的 MySurfaceView
这里就不把代码粘贴出来了,请自行添加;记得要添加
.
SurfaceView使用的注意事项:
因为
SurfaceView
允许自定义的线程操作Surface对象执行绘制方法,而你可能同时定义多个线程执行绘制,所以当你获取SurfaceHolder
中的Canvas
对象时记得加同步操作,避免两个不同的线程同时操作同一个Canvas
对象,当操作完成后记得调用SurfaceHolder.unlockCanvasAndPost()
方法释放掉Canvas锁
。在调用
doDraw()
执行绘制时,因为SurfaceView的特点,它会保留之前绘制的图形,所以你需要先清空掉上一次绘制时留下的图形。(View则不会,它默认在调用View.onDraw()
方法时就自动清空掉视图里的东西)。记得在回调方法:
onSurfaceDestroyed()
方法里将后台执行绘制的DrawThread
关闭,这里是使用join()
方法。这涉及到线程如何关闭的问题,多数人建议是通过一个标志位:isRunning来判断线程是否该停止运行,如果你想关闭线程只需要将isRunning改成false即可,线程会自动执行完run方法后退出。为了充分利用不同平台的资源,发挥平台的最优效果可以通过SurfaceHolder的setType函数来设置绘制的类型
目前接收如下的参数:
SURFACE_TYPE_NORMAL
:用RAM缓存原生数据的普通SurfaceSURFACE_TYPE_HARDWARE
:适用于DMA(Direct memory access )引擎和硬件加速的SurfaceSURFACE_TYPE_GPU
:适用于GPU加速的SurfaceSURFACE_TYPE_PUSH_BUFFERS
:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。
- 一个SurfaceView只在
SurfaceHolder.Callback.surfaceCreated()
和SurfaceHolder.Callback.surfaceDestroyed()
调用之间是可用的,其他时间是得不到它的Canvas对象的。