SurfaceView原理理解和常见问题

在Android系统中,有一种特殊的视图,称为SurfaceView。

什么场景选用SurfaceView

SurfaceView概念:
SurfaceView本身是一个View,符合一切View的特性,需要通过Canvas画布绘制。
developer官方文档对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.

总结几个要点:

  1. SurfaceView拥有独立的Surface(绘图表面)
  2. SurfaceView是用Zorder排序的,他默认在宿主Window的后面,SurfaceView通过在Window上面“挖洞”(设置透明区域)进行显示

SurfaceView与View的区别

  • View的绘图效率不高,主要用于动画变化较少的程序
  • SurfaceView 绘图效率较高,用于界面更新频繁的程序
  • SurfaceView拥有独立的Surface(绘图表面),即它不与其宿主窗口共享同一个Surface。
    一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。
    因此SurfaceView的UI就可以在一个独立的线程中进行绘制,可以不会占用主线程资源。
  • SurfaceView使用双缓冲机制,播放视频时画面更流畅

什么是双缓冲机制

在运用时可以理解为:SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。
当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas 。例如,如果你已经先后两次绘制了视图A和B,那么你再调用 lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的 A 视图上传,那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上,原来的B则转换为backCanvas。
相当与多个线程,交替解析和渲染每一帧视频数据。

使用场景

所以SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。常用于画面内容更新频繁的场景,比如游戏、视频播放和相机预览。

使用SurfaceView的三步骤

如何使用SurfaceView呢?

1、获取SurfaceHolder对象,其是SurfaceView的内部类。添加回调监听Surface生命周期。

mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);

2、surfaceCreated 回调后启动绘制线程
只有当native层的Surface创建完毕之后,才可以调用lockCanvas(),否则失败。

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mDrawThread = new DrawThread();
        mDrawThread.start();
    }

3、绘制

      Canvas canvas = mSurfaceHolder.lockCanvas();
      // 使用canvas绘制内容 
      ...
      mSurfaceHolder.unlockCanvasAndPost(canvas);

使用SurfaceView不显示问题

发生这种问题的原因是多层嵌套被遮挡
解决方法是根据具体情况调用如下api接口:

setZOrderOnTop(boolean onTop) // 在最顶层,会遮挡一切view
setZOrderMediaOverlay(boolean isMediaOverlay)// 如已绘制SurfaceView则在surfaceView上一层绘制。

看下他们的源码:

    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
        mSubLayer = isMediaOverlay
            ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
    }

    public void setZOrderOnTop(boolean onTop) {
        if (onTop) {
            mSubLayer = APPLICATION_PANEL_SUBLAYER;
        } else {
            mSubLayer = APPLICATION_MEDIA_SUBLAYER;
        }
    }

两个方法都是给mSubLayer赋值,所以需要注意这两个接口同时调用后一个会覆盖前一个的效果。

黑色背景问题

    //设置背景透明
    mHolder.setFormat(PixelFormat.TRANSPARENT);

你可能感兴趣的:(SurfaceView原理理解和常见问题)