Activity启动----setView之后(View的绘制过程)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity启动----setView之后(View的绘制过程)相关的知识,希望对你有一定的参考价值。
上一篇文章最后结束在RootViewImpl.setView()函数,这个函数之后发生了什么事情,我们接着分析。
1. RootViewImpl简介
ViewRootImpl作为视图层次中的顶层,实现了View和WindowManager之间需要的协议,与SystemServer进程的WindowManagerService有交互,具体实现了WindowManagerGlobal内部的大部分功能。
1.1 ViewRootImpl的定义:
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { ...... final IWindowSession mWindowSession; final W mWindow; //W继承于IWindow.Stub类。 View mView; // 由setView()函数赋值。 private final Surface mSurface = new Surface(); //mSurface可能存在多线程访问,需要加锁保护。 ...... public ViewRootImpl(Context context, Display display) { ...... mWindowSession = WindowManagerGlobal.getWindowSession(); mWindow = new W(this); ...... } }
mWindowSession将被用于建立Activity的ViewRootImpl和WindowManagerService的关系。创建代码如下:
getWindowSession()@WindowManagerGlobal.java
public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); //得到WindowMangerService的Binder代理. sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext()); ///openSession操作是一个使用Binder通信的跨进程调用。
...... } catch (RemoteException e) { ...... } } return sWindowSession; } }
getWindowManagerService()@WindowManagerGlobal.java
public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); } return sWindowManagerService; } }
openSession()@WindowManagerService.java
public IWindowSession openSession(IInputMethodClient client, IInputContext inputContext) { ...... Session session = new Session(this, client, inputContext); return session; }
可以看到,通过WindowManagerService.openSession()获得一个Session对象。类Session继承于IWindowSession.Stub,并实现了IBinder接口。它支持Binder通信,并属于Bn端,即响应请求的服务端。
1.2 ViewRootImpl与WindowManagerService的关系
ViewRootImpl通过mWindowSession(IWindowSession)与WMS进程进行跨进程通信。
ViewRootImpl的mWindow是基于Binder通信的类,而且W是IWindow的Bn端,用于响应请求。
它们之间属于跨进程通信,关系如下图:
每个APP进程都会和WMS建立一个IWindowSession会话,APP进程利用这个会话与WMS通信。WMS利用IWindow进行事件通知,每当发生一些事件(按键触摸事件)时,WMS就会告诉某个IWindow.
事件分发大致流程:
(1) WMS所在的SystemServer进程接收到按键事件。
(2) WMS找到UI位于屏幕顶端的进程所对应的IWindow对象,这个是Bp端对象。
(3) 调用这个IWindow对象的dispatchKey。 IWindow对象的Bn端位于ViewRootImpl中,ViewRootImpl根据内部View的位置信息找到真正处理这个事件的View,最后调用dispatchKey函数完成事件处理。
每个按键事件都会被转化成一个消息,由系统将这个消息加入到对应进程的消息队列中。该进程在处理消息派发时,根据消息的句柄找到对应的Window,进而由Window处理掉这个消息。
2. RootViewImpl.setView()函数
setView()@RootViewImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; //传进来的view是PhoneWindow的DecorView,保存起来。 mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); ...... mSoftInputMode = attrs.softInputMode; // InputMode可以影响输入法弹出时视图应该怎么显示。 ...... mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; */ //在被加到WindowManager之前调度第一次layout,确保收到系统事件之前重新进行了布局。 requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); //创建InputChannel对象 } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, //注意第一个参数是mWindow. 继承IWindow.Stub类。 getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { ...... }
// 创建InputQueue和WindowInputEventReceiver与窗口输入事件相关的对象。 if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; // 根据res设置touch mode。 mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; // 根据res设置visible属性。 ...... } } }
3. performTraversals()函数
requestLayout()函数的功能就是触发UI绘制。结合时序图。调用3至调用15,是消息发送传递及Callback设置过程。最后会由TraversalRunnable的run函数中调用RootViewImpl.doTraversal()函数去调用performTraversals()开始进行UI绘制。
performTraversals()函数很复杂,我们先专注于时序图展示的内容。
performTraversals()@RootViewImpl.java
private void performTraversals() { ...... final View host = mView; if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) {
......
try {
......
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}
......
if (!mStopped) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); //获取测量模式。lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENTint childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 根据测量模式进行测量。 ......
layoutRequested = true; } } } final boolean didLayout = layoutRequested && !mStopped; boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; if (didLayout) { performLayout(lp, desiredWindowWidth, desiredWindowHeight); // By this point all views have been sized and positioned ...... } ...... boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() || viewVisibility != View.VISIBLE; if (!cancelDraw && !newSurface) { if (!skipDraw || mReportNextDraw) { ......
performDraw(); } }
...... }
3.1 relayoutWindow()函数
relayoutWindow()@ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { ...... // 调用WMS的relayout函数。通过该函数修改一个window的参数,提供新的参数,它将返回屏幕上window的新的一帧(忽略位置),和该window的surface。如果window当前是隐藏的,那么surface是无效的,否则就可以使用它来画window的内容。 int relayoutResult = mWindowSession.relayout( mWindow, // 需要修改参数的window mSeq, // 排序的序列号 params, // 要应用到window的新参数 (int) (mView.getMeasuredWidth() * appScale + 0.5f), //window想要的宽 (int) (mView.getMeasuredHeight() * appScale + 0.5f), //window想要的高 viewVisibility, // window的root view的可见性 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, // outFrame, 放着屏幕上新position/size的Rect mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface // 存放新显示surface的对象。 ); ...... return relayoutResult; }
3.2 performMeasure()
在执行performMesure()时,需要传入测量模式,这里简单介绍下MeasureSpec。MeasureSpec封装了从父view传 给子view的布局要求,每个MeasureSpec代表对宽度或高度的要求。每个MeasureSpec值由大小和模式组成,高16位表示mode,低 16位表示size,即父view的size。
MeasureSpec有三种可能的模式:
UNSPECIFIED: 父view没有强加任何限制给子view,子view可以有它想要的size。
EXACTLY:父view决定子view的确切size,子view将被指定这些边界,而忽视它想要的size.
AT_MOST: 父view对子view没有限制,子view可以获得它自己指定的size。
对于DecorView来说,mode一般为EXACTLY,size为屏幕的宽高。
看下父view中是如何得到MeasureSpec的:
getRootMeasureSpec()@ViewRootImpl.java
// 根据layout params计算window中root view的MeasureSpec.
// windowSize: window可用的宽或高
// window的宽或高的layout params.
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can‘t resize. Force root view to be windowSize.
// MeasureSpec.EXACTLY: 父控件决定子控件的确切大小,子控件与父控件一样大,子控件要求的大小被忽视。
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
// MeasureSpec.AT_MOST:子控件的大小可以为它指定的大小。
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
performMeasure()@ViewRootImpl.java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用mView的measure。 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
measure()@View.java
// 这个函数计算出view的大小,然后设置实际的宽和高。每个view的实际宽高都是由父view和自身共同决定的。 // 实际测量工作在onMeasure()函数中执行,子类也能重载onMesure()函数实现对自身的测量。 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { // final方法,子类不可以重写 ......if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // 调用onMeasure方法, 测量自己,这个调用需要重新设置measured dimension flag。 onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ...... // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { // 子类重写onMeausre方法的时,必须调用setMeasuredDimension或者super.onMeasure方法,不然就会走到这里报错。 // setMeasuredDimension中回去改变mPrivateFlags的值 throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension }
onMeasure()@View.java
// 提供view和它的内容的测量宽高,应该由子类重写这个函数来提供准确有效的测量值。这个函数的两个参数都是由父view传过来的,代表父view的规格。 // 子类重写这个函数的时候必须调用setMeasuredDimension()保存这个view的测量宽和高,确保测量的宽和高至少是view的最小宽高。 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
setMeasuredDimension()@View.java
// 设置测量结果及flags.
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } // 赋值给mMeasuredWidth,getMeasuredWidth()函数返回的就是这个值。
// measure的目的就是对view树中的每个view的mMeasuredWidth和mMesuredHeight赋值。这两个值被赋值,该view的测量工作才算结束。 mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; // 设置flag, 这就是重写onMeasure方法时如果不调用setMeasuredDimension方法时, measure会抛出异常的原因。 mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }
View,ViewGroup,FrameLayout,DecorView的关系如下:
DecorView.onMesure()@PhoneWindow.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; final int widthMode = getMode(widthMeasureSpec); final int heightMode = getMode(heightMeasureSpec); boolean fixedWidth = false; if (widthMode == AT_MOST) { final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { final int w; if (tvw.type == TypedValue.TYPE_DIMENSION) { w = (int) tvw.getDimension(metrics); } else if (tvw.type == TypedValue.TYPE_FRACTION) { w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); } else { w = 0; } if (w > 0) { final int widthSize = MeasureSpec.getSize(widthMeasureSpec); widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(w, widthSize), EXACTLY); fixedWidth = true; } } } if (heightMode == AT_MOST) { final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { final int h; if (tvh.type == TypedValue.TYPE_DIMENSION) { h = (int) tvh.getDimension(metrics); } else if (tvh.type == TypedValue.TYPE_FRACTION) { h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); } else { h = 0; } if (h > 0) { final int heightSize = MeasureSpec.getSize(heightMeasureSpec); heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(h, heightSize), EXACTLY); } } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 调用FrameLayout.onMeasure().
int width = getMeasuredWidth(); boolean measure = false; widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); if (!fixedWidth && widthMode == AT_MOST) { final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; if (tv.type != TypedValue.TYPE_NULL) { final int min; if (tv.type == TypedValue.TYPE_DIMENSION) { min = (int)tv.getDimension(metrics); } else if (tv.type == TypedValue.TYPE_FRACTION) { min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); } else { min = 0; } if (width < min) { widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); measure = true; } } } if (measure) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
onMesure()@FrameLayout.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); // 获得子view的数目 final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { // 测量每个子View final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground‘s minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); }
// 设置自己的size, 即ViewGroup的size. 可以看到,它的size由父view及子view共同决定。 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { // 对于match parent的child进行测量。 for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
measureChildWithMargins()@ViewGroup.java
// 要求view的一个child测量自己,同时考虑MeasureSpec要求和padding和margins. protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 获取子view的LayoutParams final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); // 调整MeasureSpec,通过传进来的父view的MeasureSpec及子view自身的LayoutParams,共同决定子view的MeasureSpec. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); // 调用子view的measure(),又重新开始上面measure的流程。子view的measure()中会回调子view的onMeasure()函数。 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
getChildMeasureSpec()@ViewGroup.java
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { //获取parent view的mode和size
int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); //parent size与padding的差值(即parent中除去padding后剩余的大小),若小于0则直接返回0. int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { //根据parent mode进行处理 // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { // 若child的layout_width或者layout_height在xml或者代码中指定了大于等于0的值,
// 则设置child的size值为指定的值,mode为EXACTLY.
resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. // 若child的layout_width或者layout_height在xml或者代码中指定为MATCH_PARENT,
// 则设置child的size值为size,mode为EXACTLY.
resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can‘t be bigger than us. // 若child的layout_width或者layout_height在xml或者代码中指定为WRAP_CONTENT,
// 则设置child的size值为指定的值,mode为AT_MOST.
resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us // 下面两个分支类似
case MeasureSpec.AT_MOST: ......break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: ......break; }
//使用mode和size生成MeasureSpec返回。 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
可以看到,getChildMeasureSpec()是通过其父view提供的MeasureSpec参数得到specMode和specSize,然后根据计算出来的specMode以及子view的childDimension(layout_width或layout_height)来计算自身的measureSpec,如果其本身包含子视图,则计算出来的measureSpec将作为调用其子视图measure函数的参数,同时也作为自身调用setMeasuredDimension()的参数,如果其不包含子视图则默认情况下最终会调用onMeasure的默认实现,并最终调用到setMeasuredDimension()。
performMeasure()小结:
(1) mView.measure() , 即调用View.measure()方法。
(2) View.measure()内部调用onMeasure()方法,因为这里的mView是DecorView,调用的是DecorView.onMeasure(), 这个函数内部调用super.onMeasure()。因为DecorView继承于FrameLayout,所以super.onMeasure()即调用FrameLayout的onMeasure()方法。
(3) FrameLayout.onMeasure()对每个子view进行遍历测量,对每个子view调用measureChildWithMargins()方法测量子view。
(4) measureChildWithMargins()内部调用getChildMeasureSpec()计算出传给子view的MeasureSpec,并调用子view的measure()方法。
(5) FrameLayout.onMeasure()中根据父view及(3)测量出来的子view的MeasureSpec信息调用setMeasuredDimension()设置自身的size。
(6) 使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取view测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。
(7) ViewGroup的子类必须要求LayoutParams继承于MarginLayoutParams,否则无法使用layout_margin参数。
3.3 performLayout()
performLayout()@ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { mLayoutRequested = false; mScrollMayChange = true; mInLayout = true; final View host = mView; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { // 把performMeasure测量的结果传入layout()函数 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); mInLayout = false; ...... } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } mInLayout = false; }
这个函数内调用mView.layout()方法,而ViewGroup中重写了layout()方法,并调用了super.layout(), 即View.layout()。
layout()@View.java
// 给view及它的子孙的size及position赋值。 // 这是布局机制的第二阶段(第一阶段是measuring)。在这个阶段,每个父view都会调用其所有子view的layout()来放置它们。 // 子类中不应该重载这个方法,若子类中有children, 则该子类应该重载onLayout()函数,在这个函数中调用每个child的layout. public void layout(int l, int t, int r, int b) { ......int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // 这部分是判断这个View的大小是否已经发生了变化,来判断是否需要重新layout。判断的方式就是通过将l,t,r,b参数传给setFrame()赋值给
// mLeft,mTop, mRight,mBottom。 // isLayoutModeOptical(): 如果mParent是一个使用可见范围进行编排的ViewGroup, 返回true. boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { // 调用onLayout方法 onLayout(changed, l, t, r, b); mPrivateFlags &= ~Android查缺补漏--Activity生命周期和启动模式