UI绘制流程四(View的布局layout及绘制draw)

234 阅读4分钟

老规矩回到ViewRootImpl的perFormLayout方法

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
            //desiredWindowWidth,  desiredWindowHeight就是屏幕的宽高
            
            ...
            //这里的host就是DecorView
            final View host = mView;
        if (host == null) {
            return;
        }
        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
            Log.v(mTag, "Laying out " + host + " to (" +
                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
        try {
        //这里调用layout方法
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}            

跟进layout方法

public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        //确定View在当前屏幕的4个顶点位置
        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) {
            //子View空实现
            onLayout(changed, l, t, r, b);
        }
        
    }    

private boolean setOpticalFrame(int left, int top, int right, int bottom) {
 
        Insets parentInsets = mParent instanceof View ?
                ((View) mParent).getOpticalInsets() : Insets.NONE;
 
        Insets childInsets = getOpticalInsets();
 
        // 内部实际上是调用setFrame()
        return setFrame(
                left   + parentInsets.left - childInsets.left,
                top    + parentInsets.top  - childInsets.top,
                right  + parentInsets.left + childInsets.right,
                bottom + parentInsets.top  + childInsets.bottom);
    }

//最终确定View本身的位置
protected boolean setFrame(int left, int top, int right, int bottom) {
        ...
    
    mLeft = left;
    mTop = top;
    mRight = right;
    mBottom = bottom;
 
    mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
 
    }

总结:子View就是通过layout计算自身的View 位置(最终调用setFrame/setOpticalFrame)

下面我们来分析下viewGroup,我们看frameLayout

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

        final int parentLeft = getPaddingLeftWithForeground();
        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();
        final int parentBottom = bottom - top - getPaddingBottomWithForeground();

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                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;
                }
                //最终调用子View的layout方法
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }
总结:View自身会调用View.layout确定自身的位置,确定mLeft,mTop,mRight,mBottom的值。如果是ViewGroup类型,需要调用onLayout确定子View的位置

接着我们看draw绘制方法,回到ViewRootImpl中的performDraw方法

private void performDraw() {
    //这里判断屏幕是否锁屏以及View是否为空
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
            return;
        } else if (mView == null) {
            return;
        }
        ...
        
         try {
            //这里面会调用到draw方法中
            boolean canUseAsync = draw(fullRedrawNeeded);
            if (usingAsyncReport && !canUseAsync) {
                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                usingAsyncReport = false;
            }
        } 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;
                }
}

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 = mSurface.lockCanvas(dirty);
        ...
        //这里我们发现初始化完成canvas(画布后),去drawColor,清屏
        //这里就是每次绘制不能保持上一次绘制的内容
        if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }
            
            ...
            //最后会调用draw方法,mView就是我们的decorView
            mView.draw(canvas);
}        

跟进mView.draw发现已经跳入View中

public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
        
        //这里官方告诉我们绘制的步骤
        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
                //如果需要,保存画布的图层以备褪色
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
                //如果需要,画出渐退的边缘并恢复图层
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        //第一步,绘制背景
        int saveCount;
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }
        // skip step 2 & 5 if possible (common case)
        //第二部或第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
            //第三部绘制内容,重写onDraw方法
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            //第四部绘制子View,只有ViewGroup才会有
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().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;
        }
        
        

我们看下ViewGroup中的dispatchDraw

protected void dispatchDraw(Canvas canvas) {
        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        
        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;
                }
            }
            
}            

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

ViewGroup是遍历子View然后一步步绘制,最终完成View树的绘制

总结

View的绘制,首先清空画布canvas.drawColor,然后

1.绘制背景drawBackgroud(canvas)2.绘制自己onDraw(canvas)3.绘制子View dispatchDraw(canvas)4.绘制前景,滚动条的装饰onDrawForeground(canvas)