从Android源码分析View绘制(3)

当view的大小已经设定完毕,则需要确定view在其父view中的位置,也就是把子view放在合理的位置上。因为只有ViewGroup才包含子view,所以一般我们说起父view,肯定是在说ViewGroup。完成布局工作主要分为两部分,也是递归实���的:

1.在layout方法中调用setFrame设置该View视图位于父视图的坐标。

2.如果view是ViewGroup类型,则调用其onLayout方法完成子view布局工作。

下面来看具体源码,父view调用了子view的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;
        }

int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        // 判断是否布局是否发生过改变,是否需要重绘。
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
        // 需要重绘。 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b); // 确定view在布局中的位置
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

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

mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }

该方法接收四个参数是子view相对于父view而言的上下左右位置。然而我们发现其中调用到的onLayout方法默认的实现是空的。这是因为确定view在布局的位置这个操作应该由Layout根据自身特点来完成。任何布局的定义都要重写其onLayout方法,并在其中设定子view的位置。

绘制

在进行完测定尺寸和定位之后,终于可以开始绘制了。这里的工作仍是通过递归来完成的。view调用draw方法来进行绘制,里面调用onDraw来绘制自身,如果还有子view则需要调用dispatchDraw来绘制子view。

绘制需要调用draw方法,总共分为六个步骤:

绘制背景

如果需要,保存canvas的层次准备边缘淡化。

绘制view的内容

绘制子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;
        // Step 1, 绘制背景
        int saveCount;

if (!dirtyOpaque) {
            drawBackground(canvas);
        }

// 如果不需要,跳过步骤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, 绘制内容
            if (!dirtyOpaque) onDraw(canvas);

// Step 4, 绘制子view
            dispatchDraw(canvas);

// Step 6, 绘制装饰部分
            onDrawScrollBars(canvas);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/613ba6669701b3956064a101df3a97f9.html