美文网首页
View的绘制流程六、draw

View的绘制流程六、draw

作者: b42f47238b43 | 来源:发表于2017-12-15 23:15 被阅读0次

前几篇文章分别讲述了View的measure、layout流程,接下来将分析三大流程的最后一步View的绘制流程。测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一个View该显示什么由绘制流程完成。
从performDraw()开始

private void performDraw() {
 //...
 final boolean fullRedrawNeeded = mFullRedrawNeeded;
 try {
  draw(fullRedrawNeeded);
 } finally {
  mIsDrawing = false;
  Trace.traceEnd(Trace.TRACE_TAG_VIEW);
 }

 //省略...
}

里面又调用了ViewRootImpl的draw方法。

private void draw(boolean fullRedrawNeeded) {
    Surface surface = mSurface;
 //...
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                return;
            }
 //省略...
}
/**
 * @return true if drawing was successful, false if an error occurred
 */
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {
    // Draw with software renderer.
    final Canvas canvas;
     //...
    mView.draw(canvas);
    //省略...
    return true;
}

可以看到最终会调用mView.draw()方法并吧canvas传递过去,canvas可以看成一个画布,View的绘制都是在这块画布上完成的。mView在之前文章中了解到是DecorView。而ViewGroup和FrameLayout都没有重写draw方法,所以我们去看View的draw方法都做了哪些事情

     ......
        /*
         * 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
         //...
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
         //...

        // Step 2, save the canvas' layers
         //...
            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }
         //...

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
         //...
        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }
         //...

        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);
        //省略...
    }

谷歌工程师已经帮我们注释好了 整个绘制流程一共分六步。
1、对View的背景进行绘制
2、保存当前的图层信息(可跳过)
3、绘制View的内容
4、对View的子View进行绘制(如果有子View)
5、绘制View的褪色的边缘,类似于阴影效果(可跳过)
6、绘制View的装饰(例如:滚动条)
其中第2步和第5步是可以跳过的,我们这里不做分析。
第一步绘制背景

private void drawBackground(Canvas canvas) {
        //获取xml中通过android:background属性或者代码中setBackgroundColor()、setBackgroundResource()等方法进行赋值的背景Drawable
        final Drawable background = mBackground;
        //...
        //根据layout过程确定的View位置来设置背景的绘制区域
        if (mBackgroundSizeChanged) {
            background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
            mBackgroundSizeChanged = false;
            rebuildOutline();
        }
           //...
            //调用Drawable的draw()方法来完成背景的绘制工作
            background.draw(canvas);
            //省略...
    }

第三步绘制自己

    /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
    }

View中该方法是一个空实现,因为不同的View有着不同的内容,这需要我们自己去实现,即在自定义View中重写该方法来实现。
第四步绘制子View

   @Override
    protected void dispatchDraw(Canvas canvas) {
        //...
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        //...
        for (int i = 0; i < childrenCount; i++) {
            //...
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        //...
        // Draw any disappearing views that have animations
        if (mDisappearingChildren != null) {
            //...
            for (int i = disappearingCount; i >= 0; i--) {
                //...
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        //省略...
    }
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

可以看出在dispatchDraw中对所有的子View都进行了遍历绘制
第六步滚动条的绘制

public void onDrawForeground(Canvas canvas) {
    onDrawScrollIndicators(canvas);
    onDrawScrollBars(canvas);
    final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
    if (foreground != null) {
        if (mForegroundInfo.mBoundsChanged) {
            mForegroundInfo.mBoundsChanged = false;
            final Rect selfBounds = mForegroundInfo.mSelfBounds;
            final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
            if (mForegroundInfo.mInsidePadding) {
                selfBounds.set(0, 0, getWidth(), getHeight());
            } else {
                selfBounds.set(getPaddingLeft(), getPaddingTop(),
                        getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
            }
            final int ld = getLayoutDirection();
            Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
                    foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
            foreground.setBounds(overlayBounds);
        }
        foreground.draw(canvas);
    }
}

绘制流程很清晰先确定绘制范围再进行绘制
到此为止View的绘制流程我们已经分析完了。

相关文章

网友评论

      本文标题:View的绘制流程六、draw

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