Android View深入学习(二),View的布局(Layout)过程

View布局最开始是从DecorView开始的,,在ViewRootImpl中的performTraversals方法中,调用了 performLayout(lp, desiredWindowWidth, desiredWindowHeight),开始对DecorView测量:

(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {mLayoutRequested = false;mScrollMayChange = true;mInLayout = true;final View host = mView;if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {Log.v(TAG, “Laying out ” + host + ” to (” +host.getMeasuredWidth() + “, ” + host.getMeasuredHeight() + “)”);}Trace.traceBegin(Trace.TRACE_TAG_VIEW, “layout”);try {host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());mInLayout = false;int numViewsRequestingLayout = mLayoutRequesters.size();if (numViewsRequestingLayout > 0) {ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,false);if (validLayoutRequesters != null) {mHandlingLayoutInLayoutRequest = true;// Process fresh layout requests, then measure and layoutint numValidRequests = validLayoutRequesters.size();for (int i = 0; i < numValidRequests; ++i) {final View view = validLayoutRequesters.get(i);Log.w(“View”, “requestLayout() improperly called by ” + view +” during layout: running second layout pass”);view.requestLayout();}measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);mInLayout = true;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’dvalidLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);if (validLayoutRequesters != null) {final ArrayList<View> finalRequesters = validLayoutRequesters;// Post second-pass requests to the next framegetRunQueue().post(new Runnable() {() {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;}

15行,host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()),调用的就是DecorView的layout方法,也就是View的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);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;}

在调用layout方法传入了四个参数,分别是该View相对于父View左,上,有,下的位置。对于DecorView来说,就是相对于窗口的位置。那么这四个参数从哪里来的? 对于最顶层DecorView来说,就是由初始位置和在View Measure中获取的测量高度和测量宽度决定的。

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

对于子View,是由子View的测量宽度和测量高度和子View的LayoutParams决定的。 在layout方法13行,调用了setFrame方法:

(int left, int top, int right, int bottom) {boolean changed = false;if (DBG) {Log.d(“View”, this + ” View.setFrame(” + left + “,” + top + “,”+ right + “,” + bottom + “)”);}if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {changed = true;// Remember our drawn bitint 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 positioninvalidate(sizeChanged);mLeft = left;mTop = top;mRight = right;mBottom = bottom;mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);mPrivateFlags |= PFLAG_HAS_BOUNDS;if (sizeChanged) {sizeChange(newWidth, newHeight, oldWidth, oldHeight);}if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {mPrivateFlags |= PFLAG_DRAWN;invalidate(sizeChanged);// parent display list may need to be recreated based on a change in the bounds// of any childinvalidateParentCaches();}// Reset drawn bit to original value (invalidate turns it off)mPrivateFlags |= drawn;mBackgroundSizeChanged = true;notifySubtreeAccessibilityStateChangedIfNeeded();}return changed;}一个有信念者所开发出的力量,大于99个只有兴趣者。

Android View深入学习(二),View的布局(Layout)过程

相关文章:

你感兴趣的文章:

标签云: