View的绘制流程

2017-09-12  本文已影响0人  dengzi_android

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()

上一篇下一篇

猜你喜欢

热点阅读