Android ViewGroup onDraw为什么没调用

ViewGroup,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用onDraw方法。

android代码一直在优化,我看了几个版本的源码,目前,我用的是API30的源码,再去看ViewGroup为什么不走onDraw()的时候,已经不是一句 if (!dirtyOpaque) 就能决定是否执行onDraw()的事了。

原因详解

在API27中,还是我们熟悉的那个 if 判断决定 onDraw()的执行

在API27以后,你会发现在draw()方法里找不到 上面这个 if 语句,那么问题来了:他是如何控制 ViewGroup 不执行 onDraw() 的呢?

这个时候,我们的目光该放在这两个片段上了,还是在 View 这个类里面

片段一:

cpp 复制代码
view.java 
/**
     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
     *
     * This is where the View specializes rendering behavior based on layer type,
     * and hardware acceleration.
     */
      boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)方法
                ...
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    dispatchDraw(canvas);
                } else {
                    draw(canvas);
                }
                ...

从这一段我们能获取两个信息:

注释:

  • ViewGroup.drawChild()调用此方法,使每个子视图都绘制自己。这是视图根据图层类型专门处理渲染行为的地方,硬件加速。
  • 是否走draw()方法由两个标志决定 mPrivateFlags & PFLAG_SKIP_DRAW

片段二 :

cpp 复制代码
public RenderNode updateDisplayListIfDirty() {
       // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        dispatchDraw(canvas);
                        drawAutofilledHighlight(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.getOverlayView().draw(canvas);
                        }
                        if (isShowingLayoutBounds()) {
                            debugDrawFocus(canvas);
                        }
                    } else {
                        draw(canvas);
                    }
}

从这一段我们能获取这么个信息:是否走draw()方法由两个标志决定 mPrivateFlags & PFLAG_SKIP_DRAW

硬件加速

现在Android默认开启硬件加速,什么是硬件加速呢?为了加快Android绘制速度,适当解放cpu资源,Android将一部分绘制放到gpu执行。而对应的Android里面的canvas,也分为是否支持硬件加速,因此绘制流程也有所差异,流程图简示如下:

\]表示该调用该类里的对应方法。 ()表示方法里的参数 从上图可以看出,不管是否开启硬件加速,都会经历"跳过绘制"的逻辑判断,而该判断的分支就决定了viewGroup的ondraw()方法是否执行。如果"跳过绘制"成立,那么调用dispatchDraw()方法,继而调用子view进行绘制(如果有子view)。如果"跳过绘制"不成立,那么调用draw(x1),该方法上面分析过了:会调用dispatchDraw()和ondraw()方法。 draw(x1)的方法如下: ```cpp public void draw(Canvas canvas) { //省略 boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // 绘制自身内容 onDraw(canvas); // 绘制子view dispatchDraw(canvas); //省略 // we're done... return; } //省略 } ``` viewGroup和View初始化时对于PFLAG_SKIP_DRAW标记做了不同的处理。 ![在这里插入图片描述](https://file.jishuzhan.net/article/1681599858764419073/90b01357ac2946299acb858249c13ec8.png) viewGroup初始化的时候,默认设置了WILL_NOT_DRAW,从字面意思来看是"不会绘制"标记,这个标记是否和PFLAG_SKIP_DRAW有联系呢?继续查看setFlags方法: ```cpp vew.java setFlags方法 //省略 if ((changed & DRAW_MASK) != 0) { if ((mViewFlags & WILL_NOT_DRAW) != 0) { if (mBackground != null || mDefaultFocusHighlight != null || (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; } else { mPrivateFlags |= PFLAG_SKIP_DRAW; } } else { mPrivateFlags &= ~PFLAG_SKIP_DRAW; } requestLayout(); invalidate(true); } //省略 ``` 到此处就比较明朗,将两个标记值联系起来了: ```cpp 1、如果设置了WILL_NOT_DRAW标记,那么继续检查background、foreground(mDrawable字段)、focusHighLight是否有值,如果三者任意一个设置了,那么将PFLAG_SKIP_DRAW标记清除,否则将该标记加上。 2、如果没有设置WILL_NOT_DRAW标记,那么将PFLAG_SKIP_DRAW标记清除。 ``` ## 如何让viewGroup onDraw()执行 既然知道了MyFrameLayout没有绘制的原因,那么就有方法让它执行绘制流程。 先来看看WILL_NOT_DRAW ```cpp view.java /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. * * Typically, if you override {@link #onDraw(android.graphics.Canvas)} * you should clear this flag. * * @param willNotDraw whether or not this View draw on its own */ public void setWillNotDraw(boolean willNotDraw) { setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); } /** * Returns whether or not this View draws on its own. * * @return true if this view has nothing to draw, false otherwise */ @ViewDebug.ExportedProperty(category = "drawing") public boolean willNotDraw() { return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW; } ``` View类里暴露了设置WILL_NOT_DRAW标记的接口: setWillNotDraw(boolean willNotDraw),可以在viewgroups里使用setWillNotDraw(false)。 不想设置该标记也是可行的,前面说过即使设置了WILL_NOT_DRAW,后面还是有判断background、foreground、focusHighLight是否有值。 background:view背景 foreground(mDrawable字段):view前景 focusHighLight:view获得焦点时高亮 我们只要设置了其中一个值,PFLAG_SKIP_DRAW标记将会被清空。 来看看这三个值如何影响PFLAG_SKIP_DRAW标记 ```cpp view.java public void setBackgroundDrawable(Drawable background) { if (background != null) { if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; requestLayout = true; } } } public void setForeground(Drawable foreground) { if (foreground != null) { if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; } } } private void setDefaultFocusHighlight(Drawable highlight) { mDefaultFocusHighlight = highlight; mDefaultFocusHighlightSizeChanged = true; if (highlight != null) { if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; } } } ``` ## 总结 ```cpp 若要ViewGroup onDraw()执行,只需要setWillNotDraw(false)、设置背景、设置前景、设置焦点高亮,4个选项其中一项满足即可。 ``` 当然也可以重写dispatchDraw()方法,在该方法里绘制自定义view的内容。

相关推荐
科昂5 分钟前
Dart 单线程异步模型:从原理到工程实践的系统化解析
android·flutter·dart
_祝你今天愉快12 分钟前
重学 Android 自定义 View 系列(十二):环形SeekBar剖析
android
Freeze-hu21 分钟前
android 下提示 SQLITECIPHER driver not loaded
android
鸿蒙布道师1 小时前
鸿蒙NEXT开发设备相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
darkchink1 小时前
[LevelDB]Block系统内幕解析-元数据块(Meta Block)&元数据索引块(MetaIndex Block)&索引块(Index Block)
android·java·服务器·c语言·数据库·c++·分布式
archko2 小时前
telophoto源码查看记录 三
android
QING6183 小时前
Activity和Fragment生命周期 —— 新手指南
android·面试·app
QING6183 小时前
Kotlin Result 类型扩展详解 —— 新手使用指南
android·kotlin·app
缘来的精彩3 小时前
kotlin 多个fragment beginTransaction容器添加使用
android·开发语言·kotlin
安小牛3 小时前
Kotlin 学习-集合
android·开发语言·学习·kotlin