当view的大小已经设定完毕,则需要确定view在其父view中的位置,也就是把子view放在合理的位置上。因为只有ViewGroup才包含子view,所以一般我们说起父view,肯定是在说ViewGroup。完成布局工作主要分为两部分,也是递归实���的:
1.在layout方法中调用setFrame设置该View视图位于父视图的坐标。
2.如果view是ViewGroup类型,则调用其onLayout方法完成子view布局工作。
下面来看具体源码,父view调用了子view的layout方法:
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
// 判断是否布局是否发生过改变,是否需要重绘。
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
// 需要重绘。 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b); // 确定view在布局中的位置
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
该方法接收四个参数是子view相对于父view而言的上下左右位置。然而我们发现其中调用到的onLayout方法默认的实现是空的。这是因为确定view在布局的位置这个操作应该由Layout根据自身特点来完成。任何布局的定义都要重写其onLayout方法,并在其中设定子view的位置。
绘制
在进行完测定尺寸和定位之后,终于可以开始绘制了。这里的工作仍是通过递归来完成的。view调用draw方法来进行绘制,里面调用onDraw来绘制自身,如果还有子view则需要调用dispatchDraw来绘制子view。
绘制需要调用draw方法,总共分为六个步骤:
绘制背景
如果需要,保存canvas的层次准备边缘淡化。
绘制view的内容
绘制子view
如果需要,绘制淡化的边缘并存储图层。
绘制装饰部分,例如滚动条等。
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
// Step 1, 绘制背景
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 如果不需要,跳过步骤2和5
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, 绘制内容
if (!dirtyOpaque) onDraw(canvas);
// Step 4, 绘制子view
dispatchDraw(canvas);
// Step 6, 绘制装饰部分
onDrawScrollBars(canvas);