Android关于Draw的整个过程我终于搞懂了

1,419 阅读2分钟

在思考mScrollX,mScrollY这两个参数对View的绘制会产生什么样的影响的时候,我看了这么个代码:

class  View{
    private void drawBackground(Canvas canvas) {
            canvas.translate(mScrollX, mScrollX);
            background.draw(canvas);
            canvas.translate(-mScrollX, -mScrollX);
    }
}

看方法名字很容易知道这是绘制背景的。但是我们在实战中会发现调用TextView.scrollBy(10f,10f)的时候,只有TextView的内容移动了位置,但是我们给TextView设置的背景并没有产生移动。带着这个疑问你看源码,在调用background.draw(canvas)之前不是调用了canvas.translate(mScrollX, mScrollX)对画布进行了移动吗??????为啥我们的背景确并没有进行移动!!!!!!!!!

哈哈,你既然看到了这里,想必你也产生了和我同样的问题。现在就带着问题来看源码,为什么我们的背景没有进行移动。

首先对整个View的绘制流程进行源码解析:

ViewRootImpl触发draw(canvas)方法。
draw(canvas)分四部走。

    1. drawBackground(canvas) : 绘制背景
    1. onDraw(canvas) : 绘制自己
    1. dispatchDraw(canvas) : 绘制孩子
    1. 绘制bar(忽略不管)

这里我先模拟这么个层级:

<Framelayout>
    <ImageView>
    </ImageView>
</Framelayout>

我来分析下:首先ViewRootImpl调用了FrameLayoutdraw(canvas)方法。然后FrameLayout调用了drawBackground绘制了FrameLayout的背景。然后调用了FrameLayoutonDraw(canvas)方法。我们知道自定义ViewGroup的时候很少几乎不会重写onDraw(canvas)方法。只有自定义View的时候才会重写。然后接着调用了FrameLayoutdispatchDraw(canvas)方法。在这个里面会遍历所有的孩子调用child.draw(canvas, this, drawingTime)可以看到到了这里就走进了子孩,在这里也就是ImageViewdraw(canvas, this, drawingTime)方法。然后在这个draw(canvas, this, drawingTime)方法里面会调用孩子的draw(canvas)方法。

注意重点来了:我们知道绘制背景的代码是在draw(canvas)这个方法里调用的。现在你想下在绘制背景前会调用canvas.translate(mScrollX, mScrollX)但是最终的现象是调用了这个方法,我们的背景并没有偏移。既然没有偏移,我们是不是可以猜想我们的画布在调用偏移前已经提前偏移过了一次,这次绘制背景前的偏移其实和之之前的偏移只是进行了一个抵消操作,所以我们的背景没有移动。哈哈,其实就是这么个过程,我们刚才分析了ViewGroupdispatchDraw(canvas)方法。他调用了child.draw(canvas, this, drawingTime)也就是这个方法draw(canvas, this, drawingTime)先于背景的偏移前进行了一次偏移。

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    int sx = 0;
    int sy = 0;
    if (!drawingWithRenderNode) {
        computeScroll();
        sx = mScrollX;
        sy = mScrollY;
    }
    final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
    final boolean offsetForScroll = cache == null && !drawingWithRenderNode;
    if (offsetForScroll) {
        canvas.translate(mLeft - sx, mTop - sy);
    }
   draw(canvas);
   return more;
}

看在倒数第四行调用了canvas.translate()对视图已经进行了偏移。
到此为什么我们的背景不会移动已经讲清楚了。