SurfaceView源码分析以及使用

概述

SurfaceView是Android中比较特殊的一类视图,它与普通的View最主要的区别是它和它的视图容器并不是在一个视图层。

为什么要使用SurfaceView

我们知道Android系统中是提供了View来进行绘图处理,然后可以通过invalidate方法通知系统去调用view.onDraw方法去对界面进行重绘,而Android系统是通过发出VSYNC信号来进行屏幕的重绘,可以看出View是主动刷新,而且这个刷新时间是16ms,如果在16ms中没有执行完需要的操作,用户就会看着卡顿,如果draw方法中需要处理的逻辑太多,或者是游戏界面,需要频繁刷新复杂的界面,这样就会阻塞主线程,从而造成画面上的卡顿。而SurfaceView,它的绘制是在另外的线程中,不会去阻塞主线程,另外它底层实现了双缓冲机制。

从网上摘录了一段对双缓冲机制的介绍

双缓冲技术是游戏开发中的一个重要的技术。当一个动画争先显示时,程序又在改变它,前面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。而双缓冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上。双缓冲主要是为了解决反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。

SurfaceView中的MVC框架

我们要了解SurfaceView,还必须要了解和它息息相关的其他两个组件:Surface和SurfaceHolder。Surface其实就视图数据,SurfaceHolder我们都知道是个接口,用来进行绘制。而SurfaceView是显示视图并且和用户交互的界面。而MVC(Model-View-Controller)框架,model是数据,也就是这里的Surface,View是用来显示的,也就是SurfaceView,而控制器,也就是这里SurfaceHolder。

分析源码

  • Surface、SurfaceHolder、surfaceView

Surface

java.lang.Object
↳ android.view.Surface

// Handle onto a raw buffer that is being managed by the screen compositor

public class Surface implements Parcelable {
          //code....
}
  • 首先看一下这个Surface类,实现了Parcelable接口进行了序列化(可以在进程中传递该类对象)用来处理屏幕显示缓冲区的数据。
  • 源码中的注释是:Handle onto a raw buffer that is being managed by the screen compositor 翻译一下就是 对由屏幕图像合成器管理的原始缓冲区进行处理,也就是用来获取原始缓冲区以及其中的内容,原始缓冲区(raw buffer)是用来保存当前窗口的像素数据,由此可知Surface就是Android用来绘图的地方,
  • 既然需要绘图,那肯定需要画布,也就是在其内部定义了Canvas对象

    private final Canvas mCanvas = new CompatibleCanvas();
    
  • CompatibleCanvas是Surface的内部类,它其中包含了一个Matrix对象,这个矩阵本质就是一块内存区域,对View的各种绘图操作就保存在这片内存中。
  • 这个内部类的作用是为了兼容Android各个分辨率的屏幕,根据不同屏幕的分辨率处理不同的图像数据。源码注释:A Canvas class that can handle the compatibility mode.
 private final class CompatibleCanvas extends Canvas {
     // A temp matrix to remember what an application obtained via {@link getMatrix}
     private Matrix mOrigMatrix = null;

     @Override
     public void setMatrix(Matrix matrix) {
         if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
             // don't scale the matrix if it's not compatibility mode, or
             // the matrix was obtained from getMatrix.
             super.setMatrix(matrix);
         } else {
             Matrix m = new Matrix(mCompatibleMatrix);
             m.preConcat(matrix);
             super.setMatrix(m);
         }
     }

     @SuppressWarnings("deprecation")
     @Override
     public void getMatrix(Matrix m) {
         super.getMatrix(m);
         if (mOrigMatrix == null) {
             mOrigMatrix = new Matrix();
         }
         mOrigMatrix.set(m);
     }
 }
lockCanvas
  • 这个方法很重要,这个方法其实并不是SurfaceView调用的,是由surfaceHolder调用。API中的解释:
  • Gets a Canvas for drawing into this surface. ( 获取当前正在绘画的canvas对象 ) After drawing into the provided Canvas, the caller must invoke unlockCanvasAndPost(Canvas) to post the new contents to the surface.(绘制完一帧数据之后,需要调用unlockCanvasAndPost释放画布,然后把绘制好的内容Post到屏幕上去显示)
  • 也就是说在绘画的时候,这个画布是被锁定的,也就是说只有当前的绘制操作完成并且画布解锁以后才能对画布进行其他的操作。
unlockCanvasAndPost
  • Posts the new contents of the Canvas to the surface and releases the Canvas. 将新绘制的内容传递给Surface,这个canvas就会释放掉
总结

Surface中提供一个可以用来处理不同屏幕分辨率的Canvas,这个Canvas就是用来提供给程序员画画的,在画画的过程当中,需要先锁定画布,只有在完成当前绘制,把数据post给Surface并且解锁画布之后,,才可以继续绘制其他的内容,原始缓冲区(raw buffer)就是用来保存数据的,而Surface的作用就类似于C++当中的句柄,得到这个句柄,就可以得到其中的Canvas、原始缓冲区以及其中内容。

SurfaceHolder

  • android.view.SurfaceHolder 是一个接口
 public interface SurfaceHolder {
 
 }
  • API解释:
  • Abstract interface to someone holding a display surface.
  • 针对显示界面Surface的抽象接口
  • Allows you to control the surface size and format, edit the pixels in the surface, and monitor changes to the surface. This interface is typically available through the SurfaceView class.
  • 允许你去控制这个Surface界面的大小、格式以及在上面绘画,并且可以监控Surface的变化,这个接口通常可以通过 SurfaceView 这个类获得(SurfaceView的getHolder()方法)这个的作用就是上面提到的Controller的角色
  • When using this interface from a thread other than the one running its SurfaceView, you will want to carefully read the methods lockCanvas() and Callback.surfaceCreated().
  • 如果使用子线程来使用这个接口,你需要非常注意 lockCanvas() and Callback.surfaceCreated()这两个方法的使用
关键接口 Callback
  • 它是SurfaceHolder内部的一个接口,有三个方法
  • public void surfaceCreated(SurfaceHolder holder); Surface第一次被创建时候调用,Surface由不可见状态到可见状态,从这个可见状态开始一直到surfaceDestroyed销毁之前,这段时间Surface对象是可以进行操作的
  • public void surfaceChanged(SurfaceHolder holder, int format, int width, int height); Surface的大小和格式改变的时候会调用,比如横竖屏切换的时候就会被调用,这个方法在surfaceCreated之后至少会被调用一次
  • public void surfaceDestroyed(SurfaceHolder holder); Surface被销毁时候调用,注意调用这个方法之后,不能够在对Surface进行操作了,否则会报错
总结

SurfaceHolder是一个接口,提供访问和控制SurfaceView中内嵌的Surface的相关方法。

SurfaceView

java.lang.Object
↳ android.view.View
↳ android.view.SurfaceView

  • 继承View ,用来显示Surface 中的数据
API注释
  • Provides a dedicated drawing surface embedded inside of a view hierarchy.
  • 在屏幕显示的视图层嵌入了一块图像专门用来显示Surface
  • the SurfaceView punches a hole in its window to allow its surface to be displayed.
  • 这个SurfaceView在window上挖了一个洞以便于Surface被显示出来
  • 很奇怪对不对,接下来继续看注释
    • The surface is Z ordered so that it is behind the window holding its SurfaceView;
      • 这个引入了Z轴,xzy轴的z轴,大家应该不陌生哈,这里是什么意思呢?也就是说SurfaceView所在的视图层级的Z 轴位置 其实是小于其宿主Activity窗口的layer的Z轴位置,也就是其实是在Activity视图层的下方的
      • 那为什么能看到SurfaceView呢???结合上面的挖了个洞,其实通俗的说就是在墙上凿了洞,洞上呢装了块玻璃,这样你就能看到后面的内容了,这里的墙其实就是Activity所在的层级
  • The Surface will be created for you while the SurfaceView’s window is visible 这个说明了动画什么时候开始,也就是当SurfaceView可见的时候,就可以在Canvas上绘制图像了,然后把数据传递给Surface用来显示在SurfaceViews上了
  • you should implement SurfaceHolder.
    Callback.surfaceCreated(SurfaceHolder) and SurfaceHolder.Callback.surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.
  • 在使用SurfaceView的地方,需要实现SurfaceHolder.Callback中的回调,以便于Surface的创建和销毁以及对其改变的监听以及响应的处理,其实处理就是Canvas开始绘制图像时候,需要将数据传递给Surface来显示

实例

如何使用SurfaceView,我写了个demo,可以下载看下,这里我就不对demo代码进行分析了。demo主要的效果就是手指在屏幕上滑动,显示出滑动轨迹。

效果图:

SurfaceViewDemo下载

猜你喜欢

转载自blog.csdn.net/YuQing_Cat/article/details/83625426