前言
View繪制流程系列文章
View的繪制流程 - onMeasure()源碼分析
View的繪制流程 - onLayout()源碼分析
View的繪制流程 - onDraw()源碼分析
結(jié)論
View的繪制流程都是從ViewRootImpl中的requestLayout()方法開(kāi)始進(jìn)去的,performMeasure()、performLayout()、performDraw(),而如果代碼中又寫(xiě)了這樣的代碼:addView()、setVisibility()等方法,意思就是會(huì)重新執(zhí)行requestLayout(),意思就是會(huì)重新執(zhí)行View的繪制流程,這個(gè)時(shí)候執(zhí)行View的繪制流程時(shí)不會(huì)和第一次一樣去執(zhí)行所有的邏輯,比如說(shuō)你自己addView(),一次性添加了10個(gè)View,那么它有可能等你添加完畢之后才去執(zhí)行 View的繪制流程的:
performLayout()調(diào)用方法順序如下:
在 ViewRootImpl中的 performLayout()方法 ->
然后調(diào)用View中的 layout()方法 ->
然后調(diào)用View中的 onLayout()方法 ,這里還是和上節(jié)課講解 onMeasure()源碼分析舉的例子一樣,還是用LinearLayout垂直的布局包裹3個(gè) TextView ->
擺放子布局, for循環(huán)所有子View,前提是子View不為 GONE,然后調(diào)用 child.layout()方法
下邊進(jìn)行分析,最下邊的結(jié)論可以不看,因?yàn)楹蜕线呥@個(gè)一樣,下邊僅用于分析流程。
1. 說(shuō)明
上節(jié)課我們看了下View繪制流程中的 onMeasure()方法,那么這節(jié)課我們就來(lái)看下onLayout()方法
View的繪制流程的入口就是 ViewRootImpl中的 requestLayout()方法,源碼如下,從requestLayout()中點(diǎn)擊進(jìn)入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()調(diào)用方法順序如下:
在 ViewRootImpl中的 performLayout()方法 ->
然后調(diào)用View中的 layout()方法 ->
然后調(diào)用View中的 onLayout()方法 ,這里還是和上節(jié)課講解 onMeasure()源碼分析舉的例子一樣,還是用LinearLayout垂直的布局包裹3個(gè) TextView ->
擺放子布局, for循環(huán)所有子View,前提是子View不為 GONE,然后調(diào)用 child.layout()方法