1.我们知道在ViewRootImpl中的performTraversals方法中,会执行view的测量、布局、绘制。
那么具体的执行流程是哪样的,是怎么调用到View中的onMeasure、onLayout、onDraw方法的。
- private void performTraversals() {
- //执行测量
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- //执行布局.
- performLayout(lp, mWidth, mHeight);
- //执行绘制
- performDraw();
- }
2.在看View测量之前,先看MeasureSpec类中定义的View mode相关的知识。
view的mode有三种:
1.UNSPECIFIED:未指定,不明确。
ViewGroup没有对子View施加任何约束,子View可以是任意大小。这种mode使用的比较少。
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
2.EXACTLY:准确的
该View必须使用父ViewGroup指定尺寸,对应Match_Parent 或者具体的数值如:36dp
public static final int EXACTLY = 1 << MODE_SHIFT;
3.AT_MOST:最多
该View的大小最大是父ViewGroup给定的尺寸,对应warp_content。
public static final int AT_MOST = 2 << MODE_SHIFT;
再看makeMeasureSpec方法。通过这个方法计算得到的measureSpec。是包含Mode和size两部分内容。
- public static int makeMeasureSpec(int size,int mode) {
- if (sUseBrokenMakeMeasureSpec) {
- return size + mode;
- } else {
- return (size & ~MODE_MASK) | (mode & MODE_MASK);
- }
- }
通过getMode可以获取View使用的mode。
- public static int getMode(int measureSpec) {
- return (measureSpec & MODE_MASK);
- }
通过getSize可以获取View的大小。
- public static int getSize(int measureSpec) {
- return (measureSpec & ~MODE_MASK);
- }
3. View的测量过程 performMeasure方法
在ViewRootImpl中childWidthMeasureSpec屏幕的宽,childHeightMeasureSpec屏幕的高。
getRootMeasureSpec这个方法,就是根据穿过来的size大小和view的模式,来计算得到的measureSpec值。
也能看出MATCH_PARENT对应 MeasureSpec.EXACTLY。
WRAP_CONTENT对应MeasureSpec.AT_MOST
- childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
-
- private static int getRootMeasureSpec(int windowSize, int rootDimension) {
- int measureSpec;
- switch (rootDimension) {
- case ViewGroup.LayoutParams.MATCH_PARENT:
- // Window can't resize. Force root view to be windowSize.
- measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
- break;
- case ViewGroup.LayoutParams.WRAP_CONTENT:
- // Window can resize. Set max size for root view.
- measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
- break;
- default:
- // Window wants to be an exact size. Force root view to be that size.
- measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
- break;
- }
- return measureSpec;
- }
mView 就是DecorView。DecoView是继承自FrameLayout-->ViewGruop-->View
mView.measure()调用的是View的measure方法
- private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
- if (mView == null) {
- return;
- }
- mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
View.Java中
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- //判断标志位,是否需要重新布局
- final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
- if (forceLayout || needsLayout) {
- //如果forceLayout为true则cacheIndex =-1;如果为false,则从缓存中获取
- int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
- //cacheIndex<0说明需要重新测量
- if (cacheIndex < 0 || sIgnoreMeasureCache) {
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- } else {
- //从缓存中获取
- long value = mMeasureCache.valueAt(cacheIndex);
- //将从缓存中获取的数据设置进来
- setMeasuredDimensionRaw((int) (value >> 32), (int) value);
- }
- }
- //将测量后的宽高,缓存起来
- mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
- (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
- }
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- //将计算后的宽高,赋值给全局变量
- setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
- getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
- }
- public static int getDefaultSize(int size, int measureSpec) {
- int result = size;
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
-
- switch (specMode) {
- case MeasureSpec.UNSPECIFIED:
- result = size;
- break;
- case MeasureSpec.AT_MOST:
- case MeasureSpec.EXACTLY:
- result = specSize;
- break;
- }
- return result;
- }
在View中,调用oMeasure方法,先调用子类的方法,也就是DecorView的onMeasure方法
在DecorView中会回调父类FrameLayout方法
在这个方法中,会不断遍历子View,进行测量
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (mMeasureAllChildren || child.getVisibility() != GONE) {
- measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
- }
- }
-
- }
measureChildWithMargins方法中,通过 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 测量子View,如果子View是一个ViewGroup,继续上面的循环遍历,测量每一个View的宽高。
- protected void measureChildWithMargins(View child,
- int parentWidthMeasureSpec, int widthUsed,
- int parentHeightMeasureSpec, int heightUsed) {
- final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
- //计算得到childView的MeasureSpec
- final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
- mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
- + widthUsed, lp.width);
- final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
- mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
- + heightUsed, lp.height);
- //调用子View的Measure方法,继续遍历
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
通过以上循环遍历,每个View的大小已经被测量出来了。但是在屏幕中摆放在什么位置,还得调用layout相关的方法。
4.ViewGroup performLayout方法执行流程。
host 就是DecorView
host.getMeasuredWidth() host.getMeasuredHeight() 得到的是测量后的结果。
- private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
- //host.getMeasuredWidth() host.getMeasuredHeight() 得到的是测量后的结果
- host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
- }
ViewGroup中的layout方法是final修饰的,在子类中无法被复写。在这调用父类View的layout方法
- @Override
- public final void layout(int l, int t, int r, int b) {
- if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
- if (mTransition != null) {
- mTransition.layoutChange(this);
- }
- //调用父类View的layout方法
- super.layout(l, t, r, b);
- } else {
- mLayoutCalledWhileSuppressed = true;
- }
- }
- View#layout
- public void layout(int l, int t, int r, int b) {
-
- //设置界面大小left,top,right,bottom 的值
- boolean changed = isLayoutModeOptical(mParent) ?
- setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
-
- //如果大小发生了变化,或者需要重新layout,则进入到onLayout的逻辑
- if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
- //调用onLayout
- onLayout(changed, l, t, r, b);
-
- //调用OnLayoutChangeListener.onLayoutChange
- 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);
- }
- }
- }
- }
设置界面大小,当大小改变时,会调用onSizeChanged方法
- protected boolean setFrame(int left, int top, int right, int bottom) {
- boolean changed = false;
- int oldWidth = mRight - mLeft;
- int oldHeight = mBottom - mTop;
- int newWidth = right - left;
- int newHeight = bottom - top;
-
- boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
- if (sizeChanged) {
- sizeChange(newWidth, newHeight, oldWidth, oldHeight);
- }
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- }
- private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
- onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
- }
在View中调用onLayout方法,其实是调用的子类的onLayout方法,看FrameLayout中。
通过layoutChildren循环遍历,调用每一个ViewGroup的onLayout方法,来确定子View在布局中的位置。
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- layoutChildren(left, top, right, bottom, false);
- }
- //通过调用layoutChildren来确定View在屏幕中的位置,
- void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final int width = child.getMeasuredWidth();
- final int height = child.getMeasuredHeight();
-
- int childLeft;
- int childTop;
-
- int gravity = lp.gravity;
- if (gravity == -1) {
- gravity = DEFAULT_CHILD_GRAVITY;
- }
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
- //计算得到距离左侧的距离
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
- lp.leftMargin - lp.rightMargin;
- break;
- case Gravity.RIGHT:
- if (!forceLeftGravity) {
- childLeft = parentRight - width - lp.rightMargin;
- break;
- }
- case Gravity.LEFT:
- default:
- childLeft = parentLeft + lp.leftMargin;
- }
- //计算得到距离顶部的距离
- switch (verticalGravity) {
- case Gravity.TOP:
- childTop = parentTop + lp.topMargin;
- break;
- case Gravity.CENTER_VERTICAL:
- childTop = parentTop + (parentBottom - parentTop - height) / 2 +
- lp.topMargin - lp.bottomMargin;
- break;
- case Gravity.BOTTOM:
- childTop = parentBottom - height - lp.bottomMargin;
- break;
- default:
- childTop = parentTop + lp.topMargin;
- }
- //调用child的layout方法,继续上面的循环遍历
- child.layout(childLeft, childTop, childLeft + width, childTop + height);
- }
- }
通过以上方法,确定了View在屏幕中的位置,接下来执行绘制流程onDraw方法。
ViewRootImpl中
- private void performDraw() {
- .....
- //fullRedrawNeeded 这个用来判断是否需要绘制全部视图
- boolean canUseAsync = draw(fullRedrawNeeded);
-
- }
- private boolean draw(boolean fullRedrawNeeded) {
-
- Surface surface = mSurface;
- //mDirty表示需要绘制的区域
- final Rect dirty = mDirty;
- //如果fullRedrawNeeded为true,把dirty大小设置为整个屏幕
- if (fullRedrawNeeded) {
- dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
- }
- .....
- //调用drawSoftware
- if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
- scalingRequired, dirty, surfaceInsets)) {
- return false;
- }
- }
- private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
- boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
-
- // Draw with software renderer.
- final Canvas canvas;
- //锁定canvas,画布大小由dirty决定
- canvas = mSurface.lockCanvas(dirty);
- //设置画布支持的密度
- //mDensity = context.getResources().getDisplayMetrics().densityDpi;
- canvas.setDensity(mDensity);
- //调用DecorView
- mView.draw(canvas);
- //提交已经绘制好的内容
- surface.unlockCanvasAndPost(canvas);
- }
mView.draw(canvas) 是真正的绘制,绘制到画布上。mView就是DecorView
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- if (mMenuBackground != null) {
- mMenuBackground.draw(canvas);
- }
- }
- View#draw
- public void draw(Canvas canvas) {
- /*
- * 绘制的6个步骤
- * 1. Draw the background. 绘制View的背景
- * 2. If necessary, save the canvas' layers to prepare for fading
- 如果有必要,会保存canvas的图层信息,可跳过
- * 3. Draw view's content. 绘制View的内容
- * 4. Draw children.绘制子View
- * 5. If necessary, draw the fading edges and restore layers
- * 如果有必要,绘制边缘并保存图层,可跳过
- * 6. Draw decorations (scrollbars for instance) 绘制View的装饰(例如:滚动条)
- */
- // Step 1, draw the background, if needed
- int saveCount;
- //1.如果需要,则绘制背景
- if (!dirtyOpaque) {
- drawBackground(canvas);
- }
- // skip step 2 & 5 if possible (common case)
- //如果可以跳过第2和第5步
- final int viewFlags = mViewFlags;
- boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
- boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
- if (!verticalEdges && !horizontalEdges) {
- // Step 3, draw the content
- //第三步,绘制内容
- if (!dirtyOpaque){
- onDraw(canvas);
- }
- // Step 4, draw the children
- //第四步,绘制子View
- dispatchDraw(canvas);
-
- // Step 6, draw decorations (foreground, scrollbars)
- onDrawForeground(canvas);
-
- // Step 7, draw the default focus highlight
- drawDefaultFocusHighlight(canvas);
-
- if (debugDraw()) {
- debugDrawFocus(canvas);
- }
-
- // we're done...
- //绘制完成,return。这样就跳过了第2和第5步的绘制。
- return;
- }
- }
1) 绘制背景:drawBackground(canvas)
绘制背景调用的是Drawable子类的draw(canvas)。这样就能够把背景绘制在画布上。
- private void drawBackground(Canvas canvas) {
- final Drawable background = mBackground;
- //如果没有background,则不进行绘制
- if (background == null) {
- return;
- }
- //如果有偏移量,先偏移画布,然后再draw
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- if ((scrollX | scrollY) == 0) {
- background.draw(canvas);
- } else {
- canvas.translate(scrollX, scrollY);
- background.draw(canvas);
- //画完之后,将画布移回去
- canvas.translate(-scrollX, -scrollY);
- }
- }
2)绘制内容:onDraw(canvas);
先调用DecorView#onDraw
- @Override
- public void onDraw(Canvas c) {
- super.onDraw(c);
-
- mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
- mStatusColorViewState.view, mNavigationColorViewState.view);
- }
super.onDraw(c)这个调用的是View的onDraw.在View中这个是空实现。
我们自定义View的时候,复写的就是这个方法。
- protected void onDraw(Canvas canvas) {
-
- }
3)绘制子View:dispatchDraw(canvas);
DecorView#dispatchDraw
- //遍历子View 进行绘制
- protected void dispatchDraw(Canvas canvas) {
- final int childrenCount = mChildrenCount;
- final View[] children = mChildren;
- for (int i = 0; i < childrenCount; i++) {
- more |= drawChild(canvas, transientChild, drawingTime);
- }
- }
- //drawChild绘制子View child.draw调用View的draw,继续上面的绘制流程
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- return child.draw(canvas, this, drawingTime);
- }
4)绘制装饰:onDrawForeground
绘制装饰就是指View除了背景、内容、子View的其他部分,例如滚动条等。
- public void onDrawForeground(Canvas canvas) {
- onDrawScrollIndicators(canvas);
- onDrawScrollBars(canvas);
- }
至此View的测量、布局、绘制流程已经梳理完成。绘制已经把View绘制在了画布canvas上。
那么画在是如何显示在屏幕上的呢?
看看 canvas = mSurface.lockCanvas(dirty);
这是锁定了一个Canvas,并把Java层的mCanvas,传递到了Native层。
- public Canvas lockCanvas(Rect inOutDirty){
- synchronized (mLock) {
- mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
- return mCanvas;
- }
- }
而调用canvas.draw方法时,其实是调用的native方法。在native层会通过SkBitmap数据,这个是通过Skia绘制引擎实现的。
private static native void nDrawColor(...)
private static native void nDrawBitmap(...)
在绘制完成后,java层会调用 surface.unlockCanvasAndPost(canvas);
这个方法会通知Native层代码,将绘制好的Bitmap发送到一个队列里,然后显示到屏幕上。