前言
View绘制流程系列文章
View的绘制流程 - onMeasure()源码分析
View的绘制流程 - onLayout()源码分析
View的绘制流程 - onDraw()源码分析
结论
View的绘制流程都是从ViewRootImpl中的requestLayout()方法开始进去的,performMeasure()、performLayout()、performDraw(),而如果代码中又写了这样的代码:addView()、setVisibility()等方法,意思就是会重新执行requestLayout(),意思就是会重新执行View的绘制流程,这个时候执行View的绘制流程时不会和第一次一样去执行所有的逻辑,比如说你自己addView(),一次性添加了10个View,那么它有可能等你添加完毕之后才去执行 View的绘制流程的:
performLayout()调用方法顺序如下:
在 ViewRootImpl中的 performLayout()方法 ->
然后调用View中的 layout()方法 ->
然后调用View中的 onLayout()方法 ,这里还是和上节课讲解 onMeasure()源码分析举的例子一样,还是用LinearLayout垂直的布局包裹3个 TextView ->
摆放子布局, for循环所有子View,前提是子View不为 GONE,然后调用 child.layout()方法
下边进行分析,最下边的结论可以不看,因为和上边这个一样,下边仅用于分析流程。
1. 说明
上节课我们看了下View绘制流程中的 onMeasure()方法,那么这节课我们就来看下onLayout()方法
View的绘制流程的入口就是 ViewRootImpl中的 requestLayout()方法,源码如下,从requestLayout()中点击进入View的 layout()方法:
ViewRootImpl源码如下:
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
}
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
} else
if (didLayout) {
performLayout(lp, mWidth, mHeight);
}
performDraw();
} else {
}
mIsInTraversal = false;
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "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) {
false);
if (validLayoutRequesters != null) {
// Process fresh layout requests, then measure and layout
int 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())
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();
}
}
});
}
}
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);
}
performLayout()调用方法顺序如下:
在 ViewRootImpl中的 performLayout()方法 ->
然后调用View中的 layout()方法 ->
然后调用View中的 onLayout()方法 ,这里还是和上节课讲解 onMeasure()源码分析举的例子一样,还是用LinearLayout垂直的布局包裹3个 TextView ->
摆放子布局, for循环所有子View,前提是子View不为 GONE,然后调用 child.layout()方法