Android 自定义 View 之 draw 原理分析

Android 自定义 View 系列文章至今已经分析了自定义流程的 measure,layout 以及对 TouchEvent 的处理。那么接下来当然应该讲到对draw原理分析了,draw这一步骤是整个自定义过程中极为重要的一步,而今天这一篇文章就是对 draw 原理进行分析。经过 measure 测量和 layou t定位后,自定义 View 便进入了 draw 绘制阶段。


android.view.View.draw()

既然是对 draw 原理分析,那么自然首先看一下 View 的 draw() 方法源码了

在看源码之前我们先看 Google 官方文档对 android.view.View.draw() 方法的描述,这有助于我们对源码的理解:

Manually render this view (and all of its children) to the given Canvas. The view must have already done a full layout before this function is called. When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method. If you do need to override this method, call the superclass version.

上面的话大约有以下几点需要注意:

  1. 在给定的画布(canvas)上手动渲染这个视图(以及它所有的子视图(子 view))

  2. 在调用此函数之前,视图必须已经完成了完整的布局

  3. 当实现一个视图时,实现o nDraw(android.graphics.Canvas)而不是覆盖(重写)这个方法

  4. 如果你确实需要重写这个方法,调用超类的版本

我们可以看到绘制(draw)的操作是在canvas(画布)上进行的,所以这里我们再来了解一下Canvas类吧!

Google官方文档对 Canvas类 的描述如下:

The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

以上信息主要有以下几点:

  1. Canvas 类保存对 draw() 方法的调用

  2. 如果要进行绘制(draw),需要四个基本组成部分

  3. 储存绘制的内容,用于保存像素的位图(Bitmap )、一个 Canvas 来承载绘制调用(写入位图,调用各种方法,如 canvas.drawLine()、canvas。drawPaint())、绘图图元(根据自己需求绘制,例如圆(Rect),路径(Path),文本,Bitmap)、画笔(Paint,描述绘图的颜色和风格)

我们可以看到 Canvas 类在 draw 过程中起着重要的作用,接下来我们直接上源码

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;

        int saveCount;

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

        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {

            if (!dirtyOpaque) onDraw(canvas);

            dispatchDraw(canvas);

            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            onDrawForeground(canvas);

            return;
        }


        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, t
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值