Android View原理解析之绘制流程(draw)

本文详细探讨了Android中View的draw流程,从View和ViewGroup的角度出发,阐述了绘制背景、内容、子View和装饰的过程。在View的draw方法中,涉及了绘制背景、内容、子View和装饰的逻辑,而ViewGroup则主要负责绘制子View。通过源码分析,帮助读者理解View的绘制机制。
摘要由CSDN通过智能技术生成

提示:本文的源码均取自Android 7.0(API 24)

前言

自定义View是Android进阶路线上必须攻克的难题,而在这之前就应该先对View的工作原理有一个系统的理解。本系列将分为4篇博客进行讲解,本文主要对View的绘制流程进行讲解。相关内容如下:

从View的角度看draw流程

在本系列的第一篇文章中讲到整个视图树(ViewTree)的根容器是DecorView,ViewRootImpl通过调用DecorView的draw方法开启布局流程。draw是定义在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.  
 * 
 * View的子类不应该重写这个方法,而应该重写onDraw方法绘制自己的内容
 *
 * @param canvas The Canvas to which the View is rendered.
 */
@CallSuper
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;

    /*
     * 完整地绘制流程将按顺序执行以下6步
     * 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 绘制子View
     * 5. If necessary, draw the fading edges and restore layers 恢复保存的图层
     * 6. Draw decorations (scrollbars for instance) 绘制装饰(比如滑动条)
     */

    // ① Step 1, 绘制背景(如果有必要的话)
    int saveCount;

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

    // 通常情况下会跳过第2步和第5步
    // skip step 2 & 5 if possible (common case)
    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);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
    
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // ④ Step 6, 绘制View的装饰 (foreground, scrollbars)
        onDrawForeground(canvas);

        // ⑤ Step 7, 绘制默认的焦点高亮
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
    
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }
    
    /* 以下会完整执行所有绘制步骤(一般不会执行到这里)
     * Here we do the full fledged routine...
     * (this is an uncommon case where speed matters less,
     * this is why we repeat some of the tests that have been
     * done above)
     */
    ........
}

这个方法的逻辑非常清晰,这里咱们再来总结一下在draw中要执行的步骤:

  1. 绘制背景
  2. 保存Canvas图层信息(如果有必要的话)
  3. 绘制View的内容
  4. 绘制子View
  5. 绘制保存的Canvas图层信息(如果有必要的话)
  6. 绘制View的装饰(比如滑动条)

其中第2步和第5步在通常情况下是不会执行的,所以我们也就不再深究它们了。首先在代码①的位置,调用了drawBackground方法绘制View的背景,那让我们首先来看看在这个方法中做了些什么:

private void drawBackground(Canvas canvas) {
    
    final Drawable background = mBackground;
    if (background == null
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值