1孽糖、View的繪制流程的開始
Android中有太多太多的方法可以開啟一個View的繪制流程楼眷,比如 view.setBackgroundColor() view.addView()等等弟孟。环肘。
LinearLayout linearLayout=new LinearLayout(this);
linearLayout.setBackgroundColor(Color.parseColor("#ff0000"));// 可以開啟View的繪制流程
linearLayout.addView(topView);// 可以開啟View的繪制流程
我們一步步來查看源碼,發(fā)現(xiàn)他們最后都調(diào)用到了View的requestLayout()方法仁讨,下面我們來看一下這個方法
public void requestLayout() {
// ...
mParent.requestLayout();
// ...
}
我們發(fā)現(xiàn)随珠,View的requestLayout() 最終調(diào)用了mParent.requestLayout();方法灭袁,這里的mParent其實就是 ViewRootImpl 這個類,為什么是這個類呢窗看? 我們來從activity的啟動來分析一下
在ActivityThread類中
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// ...
// 啟動activity 調(diào)用activity的onCreat()
Activity a = performLaunchActivity(r, customIntent);
// 調(diào)用activity的onResume()
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
// 我們可以從源碼一步步跟蹤茸歧,發(fā)現(xiàn)這個 vm 就是WindowManagerImpl
ViewManager wm = a.getWindowManager();
// 我們這里就是調(diào)用WindowManagerImpl的addView()方法
wm.addView(decor, l);
}
然后我們來看WindowManagerImpl的addView()方法
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
繼續(xù)來看 mGlobal.addView()方法(WindowManagerGlobal 類中)
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ...
ViewRootImpl root;
View panelParentView = null;
// ...
// 在這個方法創(chuàng)建ViewRootImpl類
root = new ViewRootImpl(view.getContext(), display);
// 調(diào)用ViewRootImpl類的setView()方法
root.setView(view, wparams, panelParentView);
}
繼續(xù)來到ViewRootImpl類的setView()方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// ...
requestLayout();
// ...
// 我們來看最關(guān)鍵的這個方法,這里調(diào)用了View的assignParent()方法显沈,并把ViewRootImpl類自己傳進(jìn)去
view.assignParent(this);
// ...
}
我們繼續(xù)來看View的assignParent()方法
void assignParent(ViewParent parent) {
// 這里就是將前面我們要用的mParent 置為 ViewRootImpl
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
到此我們分析了View中 mParent.requestLayout(); 其實是調(diào)用了 ViewRootImpl 的 requestLayout() 方法
下面我們著重分析 ViewRootImpl 的 requestLayout() 方法
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// ...
scheduleTraversals();
}
}
void scheduleTraversals() {
// ...
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
// ...
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
// ...
performTraversals();
// ...
}
}
我們最終調(diào)用到performTraversals()這個方法软瞎,我們View的繪制流程從這里才剛剛開始
private void performTraversals() {
// ...
// view的測量,用于指定和測量layout中所有控件的寬高
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ...
// view的擺放
performLayout(lp, mWidth, mHeight);
// ...
// view的繪制
performDraw();
}
1.1 我們先來看一下performMeasure方法
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
// ...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// ...
}
// 下面是View中的方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
// ...
// 我們的第一個比較重要的方法出現(xiàn)了(測量view的寬高)
onMeasure(widthMeasureSpec, heightMeasureSpec);
// ...
}
// 這個時候我們就需要去看具體ViewGroup的onMeasure()方法拉讯,
// 我們就用LinearLayout來做分析涤浇,其他的layout其實都一個套路,只是實現(xiàn)方式不一樣而已
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
下面我們就來看一下LinearLayout中的onMeasure()方法是如何實現(xiàn)的
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
// 豎方向的測量
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
// 橫方向的測量
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
// 我們就挑豎方向的測量來看一下
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
// ...
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
// 測量子view的寬與高
// 這個時候魔慷,子view的寬與高才有了值
measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec, usedHeight);
}
// 高度的size只锭,有一套算法,每個item在豎方向疊加所得
int heightSize = mTotalLength;
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
// 設(shè)置自己的寬與高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
}
void measureChildBeforeLayout(View child, int childIndex,
int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
int totalHeight) {
measureChildWithMargins(child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight);
}
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 獲取子類的mode與size
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
// 獲取子類的mode與size
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
// 調(diào)用子類measure方法院尔,進(jìn)一步調(diào)用子類的onMeasure()方法
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
// 結(jié)論: 子view的mode會根據(jù)父類的mode來共同決定
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 獲取父類的寬高模式
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// 父布局是一個指定的值 MeasureSpec.EXACTLY
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
// 子布局是一個指定的值纹烹,則resultSize = childDimension 并且子類的模式也是 MeasureSpec.EXACTLY;
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 子布局是一個MATCH_PARENT,則resultSize = size(這里的size為父類的size) 并且子類的模式也是 MeasureSpec.EXACTLY;
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 子布局是一個WRAP_CONTENT召边,則resultSize = size(這里的size為父類的size) 并且子類的模式也是 MeasureSpec.AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 父布局是一個自適應(yīng)布局 MeasureSpec.AT_MOST
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// 子布局是一個指定的值,則resultSize = childDimension 并且子類的模式也是 MeasureSpec.EXACTLY;
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 子布局是一個MATCH_PARENT裹驰,則resultSize = size(這里的size為父類的size) 并且子類的模式也是 MeasureSpec.AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 子布局是一個WRAP_CONTENT隧熙,則resultSize = size(這里的size為父類的size) 并且子類的模式也是 MeasureSpec.AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
1.2我們再來看一下performLayout方法
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
// ...
// 調(diào)用View的layout方法
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
// ...
}
// 然后來到View 的layout方法
public void layout(int l, int t, int r, int b) {
// ...
// 調(diào)用自己的onLayout方法
onLayout(changed, l, t, r, b);
// ...
}
// 我們發(fā)現(xiàn)這里是一個空實現(xiàn),這就要到具體的ViewGroup實現(xiàn)類中查看具體實現(xiàn)幻林,我們也是拿LinearLayout來分析
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
// 下面我們來看LinearLayout的onLayout方法
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 分來豎方向和橫方向
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}
// 我們來看豎方向的layout方法
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// ... 循環(huán)獲取子view贞盯,并擺放child View
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {// 如果子view不是GONE
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
}
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
}
}
// 設(shè)置子view的layout
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
1.3我們再來看一下performDraw方法
private void performDraw() {
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
draw(fullRedrawNeeded);
}
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
mView.draw(canvas);
return true;
}
// 然后我們調(diào)到View的draw方法
public void draw(Canvas canvas) {
// 畫背景
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 有一個判斷音念,如果是ViewGroup的話,dirtyOpaque = true躏敢,所以ViewGroup是默認(rèn)不會調(diào)用onDraw方法 通過onDraw()繪制自身內(nèi)容;
if (!dirtyOpaque) onDraw(canvas);
// 空方法dispatchDraw()
dispatchDraw(canvas);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
// 現(xiàn)在我們來看一下LinearLayout中的dispatchDraw方法
// 通過dispatchDraw()繪制子View;
protected void dispatchDraw(Canvas canvas) {
// 循環(huán)獲取子view闷愤,并調(diào)用子view的onDraw方法
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
}
}
// 調(diào)用此方法,就回調(diào)到子view的draw方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
總結(jié)一下:
1件余、總流程 : ViewRootImpl類中 requestLayout() -> scheduleTraversals() -> doTraversal() -> performTraversals() -> performMeasure() -> performLayout() -> performDraw()