借用该链接:凶残的程序员-View 的工作流程 的一张图,来表示大致的工作流程。
来一张时序图:
表格对比
layout | onLayout | layout | onLayout | ||
---|---|---|---|---|---|
ViewGroup | super.layout; 通知观察者 | null | View | 记录位置, 调onLayout | null |
ViewGroup子类 | null | 根据自身布局的 特点来确定布局, for循环 :调用 child.layout | View子类 | null | super.onLayout; 拓展自己的功能 |
// 代码段0:
// ViewRootImpl 类
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
WindowManager.LayoutParams lp = mWindowAttributes;
Rect frame = mWinFrame;
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
performLayout(lp, mWidth, mHeight);
...
// 源码段1:
// ViewRootImpl 类
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
比较简单的调用了 DecorView 的 layou 函数,流程继续。
没有重写 layou 函数,直接使用父类 ViewGroup 的 layou 函数…
// 源码段2:
// ViewGroup 类
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
通知了观察者,然后调用 super.layout(l, t, r, b);即 View#layou 函数。
// 源码段3:
// View类
/**
* Assign a size and position to a view and all of its
* descendants
*
*
* @param l Left position, relative to parent
* @param t Top position, relative to parent
* @param r Right position, relative to parent
* @param b Bottom position, relative to parent
*/
@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;
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);
// 。。。
// 通知观察者
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList)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);
}
}
}
}
在 View#layou 函数的开头就非常直接的调用 setFrame 函数,设置布局的四个关键数值:mLeft,mTop,mBottom,mRight。setOpticalFrame 函数本质也是调用 setFrame 函数。
View#setFrame 源码
// 代码段3.1:
// View类
/**
* Assign a size and position to this view.
*
* This is called from layout.
*
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
* @return true if the new size and position are different than the
* previous ones
* {@hide}
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged); // 触发重绘
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
这四个入参,表示子控件对于父控件四个边的距离,在 View 类中用四个变量保存:mLeft,mTop,mBottom,mRight。
后续的流程,比如 onDraw 函数中,可以通过 getLeft()、getTop()、getBottom()、getRight() 这四个函数获取;
public final int getLeft() {
return mLeft;
}
public final int getRight() {
return mRight;
}
...
public final int getWidth() {
return mRight - mLeft;
}
public final int getHeight() {
return mBottom - mTop;
}
再回到 View#layou 函数,执行完 setFrame 函数后,表示布局已经确定,接着调用 onLayout 函数,流程继续。
// 代码段4
// DecorView 类
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// 即调用 FrameLayout 的 onLayout 函数
super.onLayout(changed, left, top, right, bottom);
// 其他一些处理逻辑
getOutsets(mOutsets);
if (mOutsets.left > 0) {
offsetLeftAndRight(-mOutsets.left);
}
if (mOutsets.top > 0) {
offsetTopAndBottom(-mOutsets.top);
}
if (mApplyFloatingVerticalInsets) {
offsetTopAndBottom(mFloatingInsets.top);
}
if (mApplyFloatingHorizontalInsets) {
offsetLeftAndRight(mFloatingInsets.left);
}
// If the application changed its SystemUI metrics, we might also have to adapt
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
getViewRootImpl().requestInvalidateRootRenderNode();
}
}
开头处即调用求父类 FrameLayout 的 onLayout 函数…
代码段5:
// FrameLayout 类
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
// 代码段6:
// FrameLayout 类
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
layoutChildren 函数中,逐个计算好 子View 的布局,在调用 child.layout 触发 子 View 的布局流程。
从这一步开始,后续布局流程的 普通 ViewGroup的子类 和 普通 View 的子类 的 layout 和 onLayout 流程的大致相当的,都是调用 View#layout – setFrame 设定自己的布局,ViewGroup的子类在 onLayout 函数中计算好 子View 的布局,并触发子 View 的布局流程。在这过程,ViewGroup的子类可能会调用到父类的一些函数。
从 layout 这个流程看下来,发现它是一个先测自己,再测 子view 的流程;
从第8步开始的 child.layout,child 也就是 DecorView 的第一个 子View,即系统布局 R.layout.screen_title 或 R.layout.screen_simple 或 其他类似的,统一放在源码工程的 frameworks/base/core/res/res/layout/ 路径下;
R.layout.screen_title 源码 和 R.layout.screen_simple 源码
因为 R.layout.screen_title 这类的布局文件是 ViewGroup 类型的,所以第8步 child 就是它们的 根view:LinearLayout;
其实,这整个流程看下来,只是对 layout 流程有一个大概的了解,具体的View 子类和 ViewGroup 子类的 onLayout 函数都有自己的实现,需要去具体分析…
R.layout.screen_title 源码
R.layout.screen_simple 源码
ViewRootImpl 源码
DecorView 源码
FrameLayout 源码
View 源码
ViewGroup 源码
ViewStub extends View
LinearLayout 源码
TextView 源码
查源码的链接