View系列 (五) — Draw 流程详解

一、概述

在执行完 measure 流程、layout 流程后,如果我们要将 View 展示在界面上,需要执行 draw 流程
measure 流程、layout 流程 类似, draw 流程 也分为以下两种场景。

View的类型draw 过程
单一的View (如 ImageView)只绘制 View 自身
ViewGroup先绘制 ViewGroup 自身,然后遍历绘制子 View

版本: Android SDK 29
关联文章:

  1. 《View系列 (一) — Android 坐标系》
  2. 《View系列 (二) — MeasureSpec 详解》
  3. 《View系列 (三) — Measure 流程详解》
  4. 《View系列 (四) — Layout 流程详解》
  5. 《View系列 (五) — Draw 流程详解》

二、单一 View 的 Draw 过程

1. 流程图

在这里插入图片描述

2. 源码分析

View.draw() 流程主要分为6步,(其中第2步和第5步可以跳过):

  1. 如果需要,则绘制背景。
  2. 保存当前canvas层。
  3. 绘制View的内容。
  4. 绘制子View。
  5. 如果需要, 则绘制View的褪色边缘, 这类似于阴影效果。
  6. 绘制装饰, 比如滚动条。
// View.class
public void draw(Canvas canvas) {
    // ...省略代码...
    
    /*
     * 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
    drawBackground(canvas);
    
    // ...省略代码...

    // Step 2, save the canvas' layers
    // ...省略代码(保存当前canvas层)...

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

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

    // Step 5, draw the fade effect and restore layers
    // ...省略代码 (绘制 fade effect)...

    // Step 6, draw decorations (foreground, scrollbars)
    onDrawForeground(canvas);
    
	// ...省略代码...
}

drawBackground(canvas)

// View.class
private void drawBackground(Canvas canvas) {
    final Drawable background = mBackground;

	// 设置背景的边界
    setBackgroundBounds();
    
    // ...省略代码...
    
    final int scrollX = mScrollX;
    final int scrollY = mScrollY;
    if ((scrollX | scrollY) == 0) {
        background.draw(canvas);
    } else {
    	// 如果有偏移值,先进行画布偏移
        canvas.translate(scrollX, scrollY);
        // 触发 Drawable.draw() 操作
        background.draw(canvas);
        canvas.translate(-scrollX, -scrollY);
    }
}

onDraw(canvas)

// View.class
protected void onDraw(Canvas canvas) {
	// empty function
}

dispatchDraw(canvas)

// View.class
protected void dispatchDraw(Canvas canvas) {
	// empty function
}

onDrawForeground(Canvas canvas)

// View.class
public void onDrawForeground(Canvas canvas) {
    onDrawScrollIndicators(canvas); //绘制ScrollIndicator
    onDrawScrollBars(canvas); //绘制ScrollBar

    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);
        }
		// 触发Drawable.draw() 流程
        foreground.draw(canvas);
    }
}

三、ViewGroup 的 Draw 过程

1. 流程图

在这里插入图片描述

2. 源码分析

由于 ViewGroup 和 View 绘制流程相同,区别仅在于 View.dispatchDraw() 的分发不同,所以我们仅分析该方法。

// ViewGroup.class
protected void dispatchDraw(Canvas canvas) {
    boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
    // 1.获取 ViewGroup 内部的子 View 个数。
    final int childrenCount = mChildrenCount;
    final View[] children = mChildren;
    int flags = mGroupFlags;

	// ...省略涉及动画的逻辑代码...
    // ...省略其它逻辑代码...
    
    // 2.遍历子 View 
    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) {
                // 3.触发子 View 的绘制流程
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            // ...省略代码...
        }
		// ...省略代码...
    }
	// ...省略代码...
}

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
	// 触发子 View的绘制流程
    return child.draw(canvas, this, drawingTime);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值