View的布局:
两个关键步骤:
1、调用系统view.layout确定自身的位置,即确定mLeft、mTop、mRight、mButtom的值;
2、如果是ViewGroup类型,需要调用onLayout确定子view的位置;
前面几篇都分析过View的绘制始于ViewRootImpl.performTraversals,在该方法内通过调用performMeasure、performLayout、performDraw这三个方法来进行measure、layout、draw流程,那么我们就从performLayout方法开始说,我们先看它的源码:
/frameworks/base/core/java/android/view/ViewRootImpl.java

这里我们看到调用了layout方法,传入了(0,0,host.getMeasureWidth(),host.getMeasureHeight())分别是DecorView的left\top\right\buttom四个位置,已知host就是DecorView,但是DecorView的父类FrameLayout以及GroupView都没有重写此方法,因此我们直接看View.layout


View的layout方法注释,此方法不能被重写,子类应该重写onLayout方法来实现view的布局;
接着我们看到有一个isLayoutModeOptical方法的判断,

根据不同的判断结果调用不同的设置方法,setOpticalFrame或者setFrame,这里不细究,我们看setOpticalFrame源码:

也是调用setFrame,所以直接看setFrame源码

这里我们看到它对mLeft、mTop、mRight、mBottom这四个值进行了初始化,对于每一个View,包括ViewGroup来说,以上四个值保存了Viwe的位置信息,所以这四个值是最终宽高,也即是说,如果要得到View的位置信息,那么就应该在layout方法完成后调用getLeft()、getTop()等方法来取得最终宽高,如果是在此之前调用相应的方法,只能得到0的结果,所以一般我们是在onLayout方法中获取View的宽高信息。
继续看layout方法,后又执行了onLayout方法

此方法是一个空方法,也就是说需要子类去实现此方法,那我们就去看ViewGroup是怎么实现此方法的

这里并没有实现此方法,而是一个抽象方法,也就是说,如果是自定义继承自View Group的View自己去实现它,由于不同的View实现方式你一样,所以这里我们还是以DecorView为例子讲解;
/frameworks/base/core/java/com/android/internal/policy/DecorView.java

DecorView首先调用了父类的onLayout,那就去它的父类FramLayout去看:
/frameworks/base/core/java/android/widget/FrameLayout.java

所以我们直接看layoutChildren方法:


由源码看出,onLayout方法内部直接调用了layoutChildren方法,而layoutChildren则是具体的实现。
先梳理一下以上逻辑:首先先获取父容器的padding值,然后遍历其每一个子View,根据子View的layout_gravity属性、子View的测量宽高、父容器的padding值、来确定子View的布局参数,然后调用child.layout方法,把布局流程从父容器传递到子元素。
那么,现在就分析完了ViewGroup的布局流程,那么我们接着分析子元素的布局流程。
子View的布局流程也很简单,如果子View是一个ViewGroup,那么就会重复以上步骤,如果是一个View,那么会直接调用View#layout方法,根据以上分析,在该方法内部会设置view的四个布局参数,接着调用onLayout方法,我们看看View.onLayout方法:

这是一个空方法,也就是说需要自定义View自己去实现,不同的View实现方式不同,这里就不分析了。
网友评论