面试题之View的绘制流程

前言

view的绘制方法:

  • measure:判断是否需要重新计算View的大小
  • layout:判断是否需要重新计算View的位置
  • draw:判断是否需要重新绘制View

源码分析

这次分析源码我们就从Activity的启动流程那里分析,当Activity初始化Window和将布局添加到PhoneWindow的内部类DecorView类之后,ActivityThread类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口,来看看handlerResumeActivity方法的实现:

public final class ActivityThread extends ClientTransactionHandler {
    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //这里调用了WindowManager的addView()方法,而我们知道WindowManager的具体实现类是WindowManagerImpl,我们到WindowManagerImpl中查看addView()的实现
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
        }
     ...
    }
}

public final class WindowManagerImpl implements WindowManager {
     @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //这里调用了WindowManagerGlobal类的addView()方法
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

public final class WindowManagerGlobal {
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            ViewRootImpl root;
            View panelParentView = null;
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                //这里调用了ViewRootImpl的setView()方法
                root.setView(view, wparams, panelParentView);
            } 
      }
}

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...
                //这里终于到了我们今天要开始分析的真正的主角
                requestLayout();
                ...
    }
}

从上面代码中我们可以得知在ActivityThread类的handleResumeActivity()方法中,我们将DecorView视图添加到了WindowManager中,而在WindowManagerGlobal中又将ViewRootImplDecorView进行了绑定,并且在ViewRootImpl中调用了requestLayout()代码,接下来我们来分析ViewRootImplrequestLayout()的方法。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            //发送绘制View的message
            scheduleTraversals();
        }
    }
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //我们主要关注这里,发送了一个mTraversalRunnable,具体的实现就是在这个Runnable接口实现类中的run方法进行的
            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);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

    private void performTraversals() {
        ...
        if (!mStopped || mReportNextDraw) {
                    ...
                    ////获得view宽高的测量约束信息,
                    //MeasureSpec:UNSPECIFIED 一般不会对子View做任何限制,用于ListView
                    //MeasureSpec:EXACTLY 父视图对子视图指定了一个确定尺寸,比如我们子View的MATCH_PARENT,或者具体的值
                    //MeasureSpec:AT_MOST 父视图对子视图指定了一个最大尺寸,比如我们子View的WRAP_CONTENT
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
                    //执行测量操作
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        } 
        if (didLayout) {
            //执行布局操作
            performLayout(lp, mWidth, mHeight);

            // By this point all views have been sized and positioned
            // We can compute the transparent area
        }
        if (!cancelDraw && !newSurface) {
            ...
            //执行绘制操作
            performDraw();
        } 
        ...
    }
}

从以上代码我们可以得出最终的view的绘制操作是在ViewRootImpl.performTraversals()方法中进行的performMeasure()测量,performLayout()布局摆放,performDraw()绘制。那接下来我们先去看看测量操作。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            //这里调用了View的measure()方法
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
}

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ...
        if (forceLayout || needsLayout) {
            ...
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                //回调到我们自己需要重写的onMeasure()方法
                onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
                ...
        }
        ...
    }
}

//这里因为整个Activity的测量是由DecorView开始的,而DecorView是继承自FrameLayout,那么我们来看看FrameLayout类中的onMeasure方法的实现
public class FrameLayout extends ViewGroup {
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                ////测量FrameLayout下每个子视图View的宽和高,measureChildWithMargins是在ViewGroup下面的
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            }
        }
    }
}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        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);
    }
}

ViewRootImplperformMeasure()最终执行到了Viewmeasure()方法,在该方法中调用了我们经常需要重写的onMeasure()方法,因为整个Activity的测量是由DecorView开始的,而DecorView是继承自FrameLayout,所以在FrameLayoutonMeasure()方法中又遍历了其子View,在其ViewGroupmeasureChildWithMargins()调用了子Viewmeasure()方法。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        ...
        try {  
            //在这里调用了View的layout()方法
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
            ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }
}

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    public void layout(int l, int t, int r, int b) {
        
        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        //这里通过获取父布局的摆放来判断是否需要重新摆放View的位置
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            //调用onLayout()方法进行布局摆放
            onLayout(changed, l, t, r, b);
            ...
        }
        ...
    }
    //这里我们可以看到View中的onLayout()方法是个空实现,因为View没有子类,所以不需要实现摆放,我们可以去ViewGroup看看具体实现
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }
}

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    //我们这里可以看到ViewGroup这里的方法为抽象方法,需要我们自定义的ViewGruop去实现,我们可以看看系统的FrameLayout是来如何实现的
    @Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);
}

public class FrameLayout extends ViewGroup {
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
        //这里调用了摆放子布局的方法
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        //获取子View的个数
        final int count = getChildCount();
        //获取各种Padding
        final int parentLeft = getPaddingLeftWithForeground();
        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();
        final int parentBottom = bottom - top - getPaddingBottomWithForeground();
        //对子View进行循环
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                //获取子View的宽高
                final int width = child.getMeasuredWidth();
                final int height = child.getMeasuredHeight();
                //获取子view的边距
                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;
                //这里就对子View进行摆放
                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;
                }
                //同时这里又调用了子View的layout()方法
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }
}

ViewRootImplperformLayout()方法中调用了Viewlayout()方法,在该方法中调用了自身的onLayout(),而这个方法又是空实现,因为其自身没有子view,所以我们找到了继承自ViewGroupFrameLayoutonLayout()方法,layoutChildren()中进行了子viewchild.layout()方法进行子布局的摆放。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    private void performDraw() {
        ...
        try {
            //这里调用了本身的draw()方法
            boolean canUseAsync = draw(fullRedrawNeeded);
            ...
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
      ...
    }

   private boolean draw(boolean fullRedrawNeeded) {
         //drawSoftware()是关键方法
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
                    return false;
        }
        return useAsyncReport;
    }

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

        // Draw with software renderer.
        final Canvas canvas;
        try {
                ....
                canvas = mSurface.lockCanvas(dirty);
                ....
                //这里真正的调用了View的draw()方法
                mView.draw(canvas);
                ...
            } 
        } 
        return true;
    }
}
public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    public void draw(Canvas canvas) {
        ...
        // Step 1, draw the background, if needed
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }
        // 这里调用onDraw方法
        if (!dirtyOpaque) onDraw(canvas);
        // 这里调用绘制子view的方法
        dispatchDraw(canvas);
        ...
        onDrawForeground(canvas);
        if (debugDraw()) {
            debugDrawFocus(canvas);
        }
    }
    //这里的dispatchDraw()是个空实现,我们可以看看ViewGruop的实现
    protected void dispatchDraw(Canvas canvas) {

    }
}

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
        @Override
    protected void dispatchDraw(Canvas canvas) {
        ...
        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
        ...
        while (transientIndex >= 0) {
            // there may be additional transient views after the normal views
            final View transientChild = mTransientViews.get(transientIndex);
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                    transientChild.getAnimation() != null) {
                //这里调用了drawChild()绘制子View的方法
                more |= drawChild(canvas, transientChild, drawingTime);
            }
        ...
    }
    
    //这里遍历调用了子view的draw()方法
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }
}

ViewRootImplperformDraw()->draw()->drawSoftware(),在其drawSoftware()方法中调用了Viewdraw()方法,draw()方法中调用了自身的onDraw()方法,也调用了dispatchDraw()方法对子View进行遍历绘制,从ViewGroupdispatchDraw()方法中调用了drawChild(),依次对子view进行了draw()方法的绘制。

总结

今天的源码有点多,上面的流程也已经分析的很明白了,简短的总结就是通过WindowManagerDecorView传入ViewRootImpl,在ViewRootImpl中的performTraversals()方法中分别对View进行measure, layout, draw,通过ViewGroup的类分别对子View依次进行测量,摆放和绘制。

你可能感兴趣的:(面试题之View的绘制流程)