简介
  Android的窗口界面是由多个View组成的View Hierachy树形结构,WMS会从DecorView进入对整个View Hierachy进行管理控制UI的显示,因此整个View Hierachy在WMS中有一个对应的WindowState,
  普通的Android控件都是将自己绘制到宿主窗口的绘图表面上,即都是在同一块图型缓冲区操作,为什么会这样做呢?因为如果andorid把更新UI这件事交由我们自己在不同线程中更新的话,就需要大量的同步,开销很大,因此需要保证所有UI相关的操作都在一个线程中执行。所以android 就把它们放到了主线程中执行,也就是把它们全放到了同一块缓冲区。这也就是需要我们尽量避免耗时的UI操作,否则会进程无响应并且报ANR错。
这时就需要SurfaceView了
  SurfaceView终究继承了View,其内嵌着专门用于绘制的Surface(绘图表面),有着自己独立的绘图表明、缓冲区,也就是这个控件不将自己的UI数据填入到它的宿主窗口的绘图表面的图形缓冲区里面去。因此SurfaceView相当于在Window上挖个洞,用来进行绘制。所以在SurfaceView里的Surface和绘制的View是不在UI线程的View hierachy中的,创建Surface时会在SuraceFlinger内部建立对应的Layer,同时在WMS又有自己对应的WIndowState
与宿主窗口分离,因此可以不在UI线程进行渲染,也就不会阻塞主线程的事件响应。可以控制刷新频率,同时利用双缓存机制,也就是为了绘制时不会闪烁,会把要绘制的放到内存,然后再一次型绘制出来
用法:

class MySurfaceView : SurfaceView, SurfaceHolder.Callback, Runnable {
    //SurfaceHolder
    private var surfaceHolder: SurfaceHolder? = null
    private var canvas: Canvas? = null
    private var paint: Paint? = null
    private var isDrawing = false
    fun setIsDrawing(b:Boolean){
        isDrawing = b
    }
    fun getIsDrawing(): Boolean {
        return isDrawing
    }
    constructor(context: Context?) : super(context) {
        initView()
    }

    constructor(context: Context?, set: AttributeSet?) : super(
        context,
        set
    ) {
        initView()
    }

    private fun initView() { 
    //获取父类创建的SurfaceHolder,对holder进行初始化
        surfaceHolder = getHolder()
        //注册 SurfaceHolder 的回调方法
        surfaceHolder?.addCallback(this)
        //可获取焦点
        isFocusable = true
        isFocusableInTouchMode = true
        //屏幕常亮
        this.keepScreenOn = true
        paint = Paint()
        paint!!.isAntiAlias = true
        paint!!.color = Color.RED
        paint!!.textSize = 80f
        paint!!.style = Paint.Style.FILL
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        //子线程中绘制
        Thread(this).start()
    }

    override fun surfaceChanged(
        holder: SurfaceHolder,
        format: Int, width: Int, height: Int
    ) {
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        isDrawing = false
    }

    override fun run() {
        var i = 0
        while(true){
            if (isDrawing){
                draw(i++)
            }
            Thread.sleep(20)
        }
    }

    private fun draw(i: Int) {
        if (i == 100) isDrawing = false
        try {
            //锁定画布
            canvas = surfaceHolder!!.lockCanvas()
            //绘制背景
            canvas?.drawColor(Color.WHITE)
            //绘制文字
            canvas?.drawText(""+i,100f,100f, paint!!)
        } catch (e: Exception) {
            Log.d("MySurfaceView", "draw() Exception")
        } finally {
            //解锁画布
            if (canvas != null) surfaceHolder!!.unlockCanvasAndPost(canvas)
        }
    }
}
class MainActivity : AppCompatActivity() {
    var surfaceView:MySurfaceView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        surfaceView = findViewById<MySurfaceView>(R.id.sv)
    }

    fun click(view: View) {
        if (surfaceView?.getIsDrawing()!!){
            surfaceView?.setIsDrawing(false)
        }else{
            surfaceView?.setIsDrawing(true)
        }
    }
}

SurfaceView设置圆角 android surfaceview详解_缓存

双缓存机制带来的问题:

我再调式代码时遇到的问题:

SurfaceView设置圆角 android surfaceview详解_缓存_02

原因:

  当我第二次调lockCanvas()的时候,系统就会给我们第二块缓存块,这个缓存块上面并没有你刚刚画的数字1,然后你在上面画了数字2,再调用unlockCanvasAndPost的时候,第二块缓存块就被绘制到屏幕上,我们看到的就只有2,然后我们再lockCanvas()的时候就会把第一块缓存块给你,上面有数字1,我们再画3,当调用unlockCanvasAndPost的时候就会出现重叠问题,解决办法也就是上面我们调用的 canvas?.drawColor(Color.WHITE)即可进行清屏作用

覆盖问题:

解决办法:在addView之后使用SurfaceView.setZOrderOnTop(true)