surfaceView

分析SurfaceView是怎么实现的,它继承自View,但是它可以在非UI线程完成界面的绘制。

1、绘制层的创建

创建时从ViewRootperformTraversals开始的,在窗口需要刷新UI,就会调用。如果发现没有当前窗口的绘制层还没有创建,或者已经失效了,则或请求WMS服务创建新的绘制图层,并且同时会给SurfaceView创建自己的绘制层。

  • performTraversals中关于图层绘制的涉及到了2个方法的调用:
    • dispatchAttachedToWindow:在判断了当前窗口是第一次被刷新UI时,触发这个调用,它的作用是从当前窗口的顶层视图开始,通知每一个子视图它要附加到宿主窗口上去了;
    • dispatchWindowVisibilityChanged:在判断当前窗口的可见性发生了变化,触发这个调用,作用是通知每一个子视图它的宿主窗口发生了变化。
  • ViewGroup.dispatchAttachedToWindow这个并没有太多的逻辑,只是将调用分发到下面的View上去而已
  • View.dispatchAttachedToWindow,它主要是完成2件事:
    • 首先是将窗口的相关信息的对象AttachInfo保存到成员变量中;
    • 调用onAttachedToWindow
  • SurfaceView.onAttachedToWindow,2件事:
    • 通知父视图,当前正在处理的SurfaceView需要在宿主窗口的绘图表面上挖一个洞,即需要在宿主窗口的绘图表面上设置一块透明区域。
    • 调用从父类View继承下来的成员函数getWindowSession来获得一个实现了IWindowSession接口的Binder代理对象,并且将该Binder代理对象保存在SurfaceView类的成员变量mSession中。这是一个Binder的机制,通过这个Binder代理对象,完成和WMS的通信,请求WMS为自己创建绘图层。
在完成上述的操作后,ViewRoot会判断当前窗口的可见性是否发生了变化,如果变化,则走下一步。
  • ViewGroup.dispatchWindowVisibilityChanged 分发
  • View.dispatchWindowVisibilityChanged调用到onWindowVisibilityChanged
  • SurfaceView.onWindowVisibilityChanged走到updateWindow上:
    • mSurface这个就是SurfaceView的绘图层,是一个Surface对象;
    • SurfaceView所在图层可以设置,如setZOrderOnTop()
    • 在SurfaceView还没有增加到WindowManagerService服务中去,调用WMS完成图层的创建并添加。

2、挖洞

由于SurfaceView的图层是小于宿主窗口(TYPE_APPLICATION_PANEL)的,那么SurfaceView就被宿主窗口覆盖住,所以需要在宿主窗口上挖个洞,将SurfaceView显示出来。
挖洞的实质是讲上层的图层透明化。

3、绘制

SurfaceView的绘制跟普通的View绘制时一样的,不同的是SurfaceView是在自己独立的图层上,不与宿主窗口共享一个绘制图层,这样就保证了SurfaceView即可以在UI线程绘制,也可以在非UI线程绘制。

虽然SurfaceView是独立的图层,但是还是会参与到宿主窗口的绘制中,即视图的draw()dispatchDraw()都要被View流程调用到。
不同的是SurfaceView不在这里完成图像的绘制,在这里只是将图层染成了黑色而已~~~

具体的绘制时通过SurfaceHolder来实现的。
示例代码:

@Override
public void surfaceCreated(final SurfaceHolder holder) {
    Log.e("1111", Thread.currentThread().getName());

    // SurfaceHolder holder = surfaceView.getHolder();
    Canvas canvas = holder.lockCanvas();
    // start do draw on this canvas
    Paint paint = new Paint();
    paint.setColor(Color.RED);
    canvas.drawCircle(getWindowManager().getDefaultDisplay().getWidth() / 2, getWindowManager().getDefaultDisplay
            ().getHeight() / 2, 80, paint);
    // end do draw
    holder.unlockCanvasAndPost(canvas);
}

补 充:

如果要在一个绘图表面进行UI绘制,那么就顺序执行以下的操作:

  1. 在绘图表面的基础上建立一块画布,即获得一个Canvas对象。
  2. 利用Canvas类提供的绘图接口在前面获得的画布上绘制任意的UI。
  3. 将已经填充好了UI数据的画布缓冲区提交给SurfaceFlinger服务,以便SurfaceFlinger服务可以将它合成到屏幕上去。

results matching ""

    No results matching ""