1、View的绘制流程的开始
Android中有太多太多的方法可以开启一个View的绘制流程,比如 view.setBackgroundColor() view.addView()等等。。
LinearLayout linearLayout=new LinearLayout(this);
linearLayout.setBackgroundColor(Color.parseColor("#ff0000"));// 可以开启View的绘制流程
linearLayout.addView(topView);// 可以开启View的绘制流程
我们一步步来查看源码,发现他们最后都调用到了View的requestLayout()方法,下面我们来看一下这个方法
public void requestLayout() {
// ...
mParent.requestLayout();
// ...
}
我们发现,View的requestLayout() 最终调用了mParent.requestLayout();方法,这里的mParent其实就是 ViewRootImpl 这个类,为什么是这个类呢? 我们来从activity的启动来分析一下
在ActivityThread类中
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// ...
// 启动activity 调用activity的onCreat()
Activity a = performLaunchActivity(r, customIntent);
// 调用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) {
// 我们可以从源码一步步跟踪,发现这个 vm 就是WindowManagerImpl
ViewManager wm = a.getWindowManager();
// 我们这里就是调用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);
}
继续来看 mGlobal.addView()方法(WindowManagerGlobal 类中)
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ...
ViewRootImpl root;
View panelParentView = null;
// ...
// 在这个方法创建ViewRootImpl类
root = new ViewRootImpl(view.getContext(), display);
// 调用ViewRootImpl类的setView()方法
root.setView(view, wparams, panelParentView);
}
继续来到ViewRootImpl类的setView()方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// ...
requestLayout();
// ...
// 我们来看最关键的这个方法,这里调用了View的assignParent()方法,并把ViewRootImpl类自己传进去
view.assignParent(this);
// ...
}
我们继续来看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(); 其实是调用了 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();
// ...
}
}
我们最终调用到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) {
// ...
// 我们的第一个比较重要的方法出现了(测量view的宽高)
onMeasure(widthMeasureSpec, heightMeasureSpec);
// ...
}
// 这个时候我们就需要去看具体ViewGroup的onMeasure()方法,
// 我们就用LinearLayout来做分析,其他的layout其实都一个套路,只是实现方式不一样而已
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
下面我们就来看一下LinearLayout中的onMeasure()方法是如何实现的
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;
// 设置自己的宽与高
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);
// 调用子类measure方法,进一步调用子类的onMeasure()方法
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
// 结论: 子view的mode会根据父类的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;
// 父布局是一个自适应布局 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) {
// ...
// 调用View的layout方法
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
// ...
}
// 然后来到View 的layout方法
public void layout(int l, int t, int r, int b) {
// ...
// 调用自己的onLayout方法
onLayout(changed, l, t, r, b);
// ...
}
// 我们发现这里是一个空实现,这就要到具体的ViewGroup实现类中查看具体实现,我们也是拿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;
// ... 循环获取子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);
}
}
}
// 设置子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;
}
// 然后我们调到View的draw方法
public void draw(Canvas canvas) {
// 画背景
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 有一个判断,如果是ViewGroup的话,dirtyOpaque = true,所以ViewGroup是默认不会调用onDraw方法 通过onDraw()绘制自身内容;
if (!dirtyOpaque) onDraw(canvas);
// 空方法dispatchDraw()
dispatchDraw(canvas);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
// 现在我们来看一下LinearLayout中的dispatchDraw方法
// 通过dispatchDraw()绘制子View;
protected void dispatchDraw(Canvas canvas) {
// 循环获取子view,并调用子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;
}
}
}
}
// 调用此方法,就回调到子view的draw方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
总结一下:
1、总流程 : ViewRootImpl类中 requestLayout() -> scheduleTraversals() -> doTraversal() -> performTraversals() -> performMeasure() -> performLayout() -> performDraw()