SurfaceView使用介绍

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第一次创建后会立即调用该函数。
作用: 程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface

surfaceChanged()方法:
说明: 当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使用的注意事项:

  1. 因为 SurfaceView 允许自定义的线程操作Surface对象执行绘制方法,而你可能同时定义多个线程执行绘制,所以当你获取 SurfaceHolder 中的 Canvas 对象时记得加同步操作,避免两个不同的线程同时操作同一个 Canvas 对象,当操作完成后记得调用 SurfaceHolder.unlockCanvasAndPost()方法释放掉Canvas锁

  2. 在调用doDraw()执行绘制时,因为SurfaceView的特点,它会保留之前绘制的图形,所以你需要先清空掉上一次绘制时留下的图形。(View则不会,它默认在调用View.onDraw()方法时就自动清空掉视图里的东西)。

  3. 记得在回调方法:onSurfaceDestroyed()方法里将后台执行绘制的 DrawThread 关闭,这里是使用join()方法。这涉及到线程如何关闭的问题,多数人建议是通过一个标志位:isRunning来判断线程是否该停止运行,如果你想关闭线程只需要将isRunning改成false即可,线程会自动执行完run方法后退出。

  4. 为了充分利用不同平台的资源,发挥平台的最优效果可以通过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对象了。
  1. 一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated()SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的。

参考资料

猜你喜欢

转载自blog.csdn.net/weixin_42324979/article/details/112568872