上面就是控件树绘制的流程。下面介绍一下上面的各个方法:
说说最特殊的顶层View,就是ViewRootImpl#mView,他没有所谓的父控件也就没人调用dispatchDraw()去绘制他,也就不会嗲用他的draw(ViewGroup,Canvas,long)去帮他处理变换坐标。这些事都在ViewRootImpl#drawSoftware()中帮mView做了,然后再其中调用mView.draw(Canvas)。
draw(Canvas):
这个方法中的Canvas中的坐标系已经是控件的坐标系,就是已经根据该控件的mScrollX/Y,translateX/Y或者各种动画变换了坐标系,变换到了控件的坐标系。
mView.draw(Canvas){
绘制背景。为了提高效率实心(不透明的)控件的背景绘制工作将被跳过
if(!dirtyOpaque){
drawBackground(canvas);
}
/******************************************************************************************************************************
进入绘制自身内容和子控件 begin
若控件不用绘制渐变边界,则进入简便的绘制流程。
if(!verticalEdges && horizontalEdges){
if(!dirtyOpaque){//我也不太理解为什么不透明的就不用绘制。
onDraw(Canvas){
绘制自身,是个空实现,需要View或ViewGroup的子类去绘制自身的内容。就是在这个方法中你可以对Canvas做坐标变换然后绘制东西。之前在draw(ViewGroup, Canvas, long)时的坐标变换是从父控件坐标系,变换到本控件坐标系。为什么要变换等下会介绍。
}
}
/******************************************************************************************************************************
dispatchDraw() begin
***********************************************************************************************************************************/
dispatchDraw(){
如果该View不是ViewGroup,则该方法为空实现,并且有个标志可跳过这一步,为了提高绘制效率。
在这里主要是实现对子控件的遍历和绘制。
1.设置剪裁区域,让该ViewGroup的所有子控件的绘制不超出该ViewGroup的区域范围。
if(clipTopadding){
saveCount = canvas.save();
//因为在View.draw(ViewGroup, Canvas, long)中已经把坐标系转换成了内容的坐标系(因为绘制的都是绘制内容和子控件),所以你要剪的区域就是是该控件的区域,若不考虑padding,则在本控件的左上角在内容坐标系的坐标为(mScrollX,mScrollY),所以参数如下:
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, mScrollX + mRight - mLeft - mPaddingRight, mScrollY + mBottom - mTop - mPaddingBottom);
}
/*********************************************************************************************************************************************
在dispatchDraw中按指定顺序遍历绘制子控件 end
******************************************************