默认状态下,ViewGroup为什么不走onDraw()?

在自定义ViewGroup时,如果onDraw()未被调用,原因是默认ViewGroup背景透明,不满足执行onDraw的条件。解决方法包括:1)将绘制代码移到dispatchDraw();2)在构造函数中调用setWillNotDraw(false);3)设置XML背景。

一、起因

遇到这个问题是在一次自定义ViewGroup中重写了onDraw()方法,并且在onDraw中绘制了画面,但是运行过后,画面没有效果。随后自己写了一个简单的Demo,打出日志看了一下绘制流程,如下:

从上面的图片可以清楚的发现在绘制时,只是走了onMeasure,onLayout和dispatchDraw,onDraw方法却没有调用。

随后借鉴了网上的说法,在自定义ViewGroup的xml中设置了一个背景颜色,并运行打印log

这个时候就可以看见在dispatchDraw之前调用了onDraw方法。

两种代码相同的自定义ViewGroup,只是改变了在xml中ViewGroup的背景,一个就会调用onDraw而另外一个则不会,那么为什么在不改变ViewGroup的情况下不走onDraw方法呢?那么又该如何解决这个问题??

带着疑问,我们去源代码中找出原因和解决方法。

二、分析原因

众所周知,ViewGroup是继承自View的,那么直接去查看View。通过代码跟踪发现在ViewGroup中实现OnDraw方法,其实是调用View中的draw(Canvas canvas)方法,如下:

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;

        /*
         * 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
        int saveCount;

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

        
<think>我们正在讨论Android视图绘制系统中的`setFlags`方法。根据引用[3],`setWillNotDraw`方法会设置`WILL_NOT_DRAW`标志,该标志会影响视图的绘制行为。如果视图没有设置`WILL_NOT_DRAW`标志(即需要绘制),那么它将在绘制过程中调用`onDraw`方法。 用户的问题:为什么在绘制状态尚未到达`has_drawn`时,会进入`setFlags`? 首先,我们需要理解`has_drawn`的含义。在Android源码中,`has_drawn`通常表示视图是否已经执行过绘制。但是,在视图的初始化阶段,绘制状态可能还没有设置。 在视图的绘制流程中,视图的绘制是通过`View.draw`方法触发的。在绘制之前,系统会检查视图是否需要绘制,其中一个关键因素就是`WILL_NOT_DRAW`标志(通过`setFlags`设置)。 根据引用[1]和[2],视图的绘制流程会从根视图开始,递归地调用子视图的`draw`方法。在绘制过程中,每个视图都会检查自己的标志位,包括`PFLAG_SKIP_DRAW`(对应`WILL_NOT_DRAW`)和`PFLAG_DIRTY_MASK`(脏区域标志)等。 现在,回答用户的问题:为什么在绘制状态尚未到达`has_drawn`时,会进入`setFlags`? 1. **初始化阶段设置标志位**:在视图创建后、绘制之前,我们可能会设置各种标志位来优化绘制。例如,如果一个视图(如`ViewGroup`)默认会绘制任何内容,那么我们可以通过`setWillNotDraw(true)`来设置`WILL_NOT_DRAW`标志,这样在绘制流程中就会跳过该视图的`onDraw`调用(见引用[3])。这个设置通常在视图初始化时完成,即在第一次绘制之前。 2. **标志位影响绘制流程**:在绘制流程开始前,系统会检查这些标志位。例如,如果设置了`WILL_NOT_DRAW`,那么该视图的`onDraw`方法就会被调用(引用[1]中提到的`PFLAG_SKIP_DRAW`)。因此,这些标志位必须在绘制开始前设置好。 3. **为什么在未绘制时设置标志位**:因为标志位是用来控制绘制行为的,所以必须在绘制发生之前设置。例如,在Activity的生命周期中,我们可以在`onCreate`或`onResume`中设置视图的属性,这些设置包括标志位的设置,然后才会进行第一次绘制。 4. **`has_drawn`状态**:这个状态通常是在第一次绘制完成后设置的。在第一次绘制之前,视图处于未绘制状态,此时我们可以通过`setFlags`来修改标志位,以改变即将发生的绘制行为。 总结:在绘制尚未发生(即`has_drawn`为假)时,我们设置标志位是为了准备第一次绘制。这些标志位决定了视图在绘制过程中的行为,因此必须在绘制开始前设置。 引用[3]说明了`setWillNotDraw`的作用:如果视图需要绘制,设置该标志可以允许进一步的优化。默认情况下,`View`类没有设置这个标志,但`ViewGroup`设置了(因为`ViewGroup`通常只是一个容器,绘制内容)。但是,如果你重写了`onDraw`方法,就应该清除这个标志(即设置`willNotDraw`为`false`),因为你需要绘制。 因此,在绘制之前(即`has_drawn`为假时)调用`setFlags`是正常的,也是必要的,因为它为绘制流程提供了配置信息。 相关问题: 1. Android视图绘制流程中,标志位(如`WILL_NOT_DRAW`)是如何影响绘制性能的? 2. 除了`setWillNotDraw`,还有哪些方法可以优化视图的绘制性能? 3. 在哪些情况下需要重写`onDraw`方法?重写时需要注意什么?</think>### 为什么绘制状态未到达 has_drawn 时可能进入 setFlags 在 Android 视图系统中,`setFlags()` 的调用与 `has_drawn` 状态无直接因果关系。以下是关键原因分析: 1. **初始化阶段标志设置** 视图在首次绘制前(`has_drawn=false`)需通过 `setFlags()` 初始化渲染属性。例如 `ViewGroup` 默认调用 `setWillNotDraw(true)`[^3],设置 `WILL_NOT_DRAW` 标志跳过自身绘制,此时视图尚未到达 `has_drawn` 状态。 2. **动态属性变更触发** 当运行时修改视图属性(如可见性、透明度等),会触发 `setFlags()` 更新视图标志位。例如: ```java view.setVisibility(View.GONE); // 内部调用 setFlags() ``` 此操作可能发生在任何绘制周期之前,与 `has_drawn` 状态无关。 3. **脏区域标记机制** 视图属性变更时(如尺寸/位置变化),系统通过 `setFlags()` 设置 `PFLAG_DIRTY` 等标志[^2],强制下次绘制重渲染。此操作独立于当前绘制状态,仅标记需要更新的视图。 4. **SurfaceView 的特殊性** 类似 SurfaceView 的视图通过 `mHolder.lockCanvas()` 直接操作离屏缓存[^4],其绘制流程独立于普通视图树,可能提前触发标志设置。 ### 绘制流程关键点 ```mermaid graph LR A[视图创建] --> B[setFlags 初始化] B --> C{首次绘制?} C -- 否 --> D[属性变更/动画等] D --> E[setFlags 更新标志] C -- 是 --> F[执行绘制 has_drawn=true] E --> F ``` ### 优化建议 若需减少非必要 `setFlags()` 调用: 1. 避免在循环中频繁修改视图属性 2. 对批量操作使用 `ViewPropertyAnimator` 3. 对静态视图设置 `setWillNotDraw(true)`[^3] --- ### 相关问题 1. Android 视图系统中 `PFLAG_DIRTY` 标志的具体作用机制是什么? 2. 如何通过 `setWillNotDraw()` 优化 ViewGroup 的绘制性能? 3. SurfaceView 与普通 View 的绘制流程差异对标志位管理有何影响? 4. 视图属性变更时如何最小化重绘区域?
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值