我们在自定义实现一个自定义view的时候,往往需要重写它的onDraw()方法,在这个方法中,我们可以获取到它的Canvas对象,那么,这个Canvas对象是怎么得到的呢?
首先我们知道Activity的视图是由一个Window来进行显示的,具体来说是一个Phone Window对象,而每个窗口又都会包含一个Surface,而Canvas就是由这个得到的,具体流程如下:
- 当窗口需要绘制UI时,调用关联的Surface的lockCanvas方法获得Canvas
- 获得Canvas后将调用View.draw()方法将内容绘制到Canvas上
- 第二步完成之后,调用unLockCanvasAndPost方法将Canvas显示到屏幕上
而我们最需要关心的是第二步中View.draw()方法,因为在它里面调用了我们熟悉的onDraw()方法以及平常不被注意的一些方法,我们先来看它的具体实现。
1 | public void draw(Canvas canvas) { |
我们可以将View的绘制流程总结下,完整的绘制分别是以下几步:
- 背景(private,无法重写)
- 主体(onDraw())
- 子 View(dispatchDraw())
- 滑动边缘渐变和滑动条
- 前景
需要注意的有两点,因为我们最常重写的方法是onDraw()方法,但是实际上onDraw方法在View中是一个空方法,如果我们是直接继承View而不是其他的子类的话,其实super.onDraw()是可以被省略掉的,还有一点就是,dispatchDraw()方法其实在Canvas中也是一个空方法,因为View是没有子类的,我们可以看看ViewGroup中是怎么实现它的。
1 | @Override |
可以看到,ViewGrup在dispatchDraw方法中调用了drawChild方法,而drawChild方法如下:
1 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) { |
可以看到,其实里面就是调用了子View的draw方法,这实际上是一个递归的过程。
总结Window绘制UI的流程如下:
- 通过Surface获得Canvas对象
- 调用Draw方法递归地绘制(View则绘制自身,ViewGroup则绘制自身和子View)
- 显示UI