View的layout过程和draw过程

View的layout过程

下面是表达其大概思路的伪码

public void layout(int l, int t, int r, int b){
      setFrame(l ,t , r, b);
      onLayout(changed ,l , t, r, b);
}

上面的思路也很清晰,就是首先传入了l ,t, r, b四个参数,然后调用setFrame(l, t, r, b)方法,通过setFrame()方法来设定View的四个顶点的位置,这样就设定了View在父容器中的位置。接着就调用onLayout()方法,这个方法的用途就是父容器确定其中所有子View的位置。有个地方需要注意:layout(int l, int t, int r, int b)中这四个参数是谁传入的?其实,就父View的onLayout()方法中调用子view的layout()方法时传入的。
同样,由于onLayout()的具体实现和具体的布局有关,所以View和ViewGroup均没有真正地实现onLayout()方法,总体来说,伪码实现如下:

protected void onLayout(boolean changed, int l, int t, int r, int b){
      for (int i = 0; i < count; i ++){
            View child = getVirtualChildAt(i);
            ......   //   获取对应child的childLeft, childTop , childRight, childBottom数据
            child.layout(childLeft, childTop , childRight, childBottom);       //注解一
      }
}

注解一:
注意这里的childLeft, childTop , childRight, childBottomd都是需要根据具体情况计算而来的。

View的draw过程

View的draw过程比较简单,下面使用伪码来表明整个过程

public void draw(Canvas canvas){
      ...
      //step 1 绘制背景
      drawBackground(canvas);
      //step 2 绘制自己
      onDraw(canvas);
      //step 3 绘制子View
      dispatchDraw(canvas);         //注解一
      //step 4 绘制装饰
      onDrawScrollBars(canvas);
      ...
      return;
}

//注解一
View绘制过程的传递是通过dispatchDraw()来实现的,dispatchDraw()会遍历所有的子元素的draw()方法,如此draw事件就一层层地传递下去了。
还有一个地方需要注意,View有一个特殊的方法setWillNotDraw():

public void setWillNotDraw(boolean willNotDraw){
      setFlags(willNotDraw ? WILL_NOT_DRAW : 0 , DRAW_MASK);
}

如果一个View不需要绘制任何内容,那么设置这个标志位为true以后,系统就会进行相应的优化。默认情况下,View没有启用这个标志位,但是ViewGroup会默认启用这个优化标志位。这个标志位的实际开发意义:当我们自定义控件继承ViewGroup并且本身不具备绘制功能时,就可以开启这个标记位从而便于系统进行后续的优化。如果知道一个ViewGroup需要通过onDraw()来绘制自身内容时,我们需要显式地关闭WILL_NOT_DRAW这个标记位。

你可能感兴趣的:(View的layout过程和draw过程)