在上一篇我们分析了view的measure()流程,当我们把view测量出来以后,接着就要算出这个view的在这个屏幕的具体位置,所以这一篇我们接着分析layout()流程,废话不多说,还是先来一张流程图,下面会根据这个流程图进行讲解
-
注意:RootView是一个FrameLayout,所以也是一个ViewGroup
-
-
layout()过程和measure()过程一样,起点都是在ViewRootImpl中的performTraverals()方法,在performTraverals()中我们又调用了performLayout(),所以我们直接从performLayout()方法开始分析,先贴上performLayout()的源码:
-
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
/**省略部分代码
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// Check the valid requests again, this time without checking/clearing the
// layout flags, since requests happening during the second pass get noop'd
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
// Post second-pass requests to the next frame
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during second layout pass: posting in next frame");
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
-
-
这里的的host就是我们的根布局DecorView,因为DecorView是一个FrameLayout,而layout()是view中被修饰final的方法,所以我们这里调用的是View的layout的方法,layout的四个参数代表view的四个顶点的位置,默认的DecorView左边距屏幕的距离和上边距屏幕的边距都是0,接着贴下View的layout方法
@SuppressWarnings({"unchecked"})
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;
/**
* setFrame()方法执行以后,View的四个顶点的距离我们就确定了
*/
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);
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;
}
-
-
当执行setFrame()方法后,会直接初始化mLeft , mTop ,mRight,mBottom这几个值,因为我们一般在view中调用 getLeft() getTop() getRight() getBottom()获取的就是这几个值,所以我们一般必须得等layout()结束以后才能获取到这几个值
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
/** 省略部分代码
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
/** 省略部分代码
}
-
-
接着继续执行我们的onLayout()方法,onLayout()方法是view中的方法,在view中是一个默认空实现,在viewGroup是一个抽象方法(继承viewGroup必须实现,因为要对子view进行布局),所以我们分两种情况来介绍
ViewGroup的onLayout(),以RelativeLayout为例
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// The layout has actually already been performed and the positions
// cached. Apply the cached values to the children.
//遍历子view
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams st =
(RelativeLayout.LayoutParams) child.getLayoutParams();
//调用每个childView的layout()方法,继续走上面的layout方法
childView.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
}
}
-
View的onLayout(),默认是一个空实现,一般情况下我们自定义控件的时候,会去重写它,实现自己的逻辑。
view中的onLayout():
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
-
-
好了,差不多就这么多了,到了这一步基本确定了view的具体位置,接着就是绘制了…..