美文网首页
View的绘制流程

View的绘制流程

作者: dengzi_android | 来源:发表于2017-09-12 10:00 被阅读0次

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

    相关文章

      网友评论

          本文标题:View的绘制流程

          本文链接:https://www.haomeiwen.com/subject/tnoqsxtx.html