Android ViewGroup onDraw为什么没调用

这篇具有很好参考价值的文章主要介绍了Android ViewGroup onDraw为什么没调用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ViewGroup,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用onDraw方法。
android代码一直在优化,我看了几个版本的源码,目前,我用的是API30的源码,再去看ViewGroup为什么不走onDraw()的时候,已经不是一句 if (!dirtyOpaque) 就能决定是否执行onDraw()的事了。
原因详解
在API27中,还是我们熟悉的那个 if 判断决定 onDraw()的执行
Android ViewGroup onDraw为什么没调用,android

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

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

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

片段二 :

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,也分为是否支持硬件加速,因此绘制流程也有所差异,流程图简示如下:
Android ViewGroup onDraw为什么没调用,android

[]表示该调用该类里的对应方法。
()表示方法里的参数
从上图可以看出,不管是否开启硬件加速,都会经历“跳过绘制”的逻辑判断,而该判断的分支就决定了viewGroup的ondraw()方法是否执行。如果“跳过绘制”成立,那么调用dispatchDraw()方法,继而调用子view进行绘制(如果有子view)。如果“跳过绘制”不成立,那么调用draw(x1),该方法上面分析过了:会调用dispatchDraw()和ondraw()方法。

draw(x1)的方法如下:

    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标记做了不同的处理。
Android ViewGroup onDraw为什么没调用,android
viewGroup初始化的时候,默认设置了WILL_NOT_DRAW,从字面意思来看是“不会绘制”标记,这个标记是否和PFLAG_SKIP_DRAW有联系呢?继续查看setFlags方法:

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);
        }
        //省略

到此处就比较明朗,将两个标记值联系起来了:

1、如果设置了WILL_NOT_DRAW标记,那么继续检查background、foreground(mDrawable字段)、focusHighLight是否有值,如果三者任意一个设置了,那么将PFLAG_SKIP_DRAW标记清除,否则将该标记加上。
2、如果没有设置WILL_NOT_DRAW标记,那么将PFLAG_SKIP_DRAW标记清除。

如何让viewGroup onDraw()执行

既然知道了MyFrameLayout没有绘制的原因,那么就有方法让它执行绘制流程。
先来看看WILL_NOT_DRAW

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标记

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;
            }
      }
    }

总结

若要ViewGroup onDraw()执行,只需要setWillNotDraw(false)、设置背景、设置前景、设置焦点高亮,4个选项其中一项满足即可。

当然也可以重写dispatchDraw()方法,在该方法里绘制自定义view的内容。文章来源地址https://www.toymoban.com/news/detail-588186.html

到了这里,关于Android ViewGroup onDraw为什么没调用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android组件化架构开发--为什么要使用组件化?组件分层?组件路由的简单实现。

    1.1 单工程项目结构 一般我们都是一个业务建一个包 缺点: 各种业务代码混杂在同一个模块里,开发人员在开发、调测过程的效率越来越低,定位某个业务问题,需要在多个业务代码混合的模块中寻找和跳转。 需要了解各个业务的功能,避免代码的改动影响其它业务的功能

    2024年02月10日
    浏览(64)
  • android studio创建一个新的项目为什么默认是kotlin语言而选择不了java语言

    关于android studio语言选择的问题。 我在进入android studio为什么创建一个新项目之后选择不了java语言有什么办法可以解决。 解决办法:这个模式下选着一个Empty Activity模块就可以使用java语言。 这对于刚刚接触anaroid studio新手比较管用。  

    2024年02月11日
    浏览(57)
  • JavaScript——为什么静态方法不能调用非静态方法

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,正逐渐往全干发展 📃 个人状态: 研发工程师,现效力于中国工业软件事业 🚀 人生格言: 积跬步至千里,积小流成江海 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒

    2024年02月11日
    浏览(60)
  • 为什么hooks不能在循环、条件或嵌套函数中调用

    hooks不能在循环、条件或嵌套函数中调用 为什么? // 20231120更新 我觉得还是先把答案说出来,再去看为什么比较合适。 其实就是一句话,因为函数组件渲染分两种场景 一、首次渲染 二、更新组件 原因就是,函数组件在首次渲染的时候,碰到hook语句时,是去创建一个空的

    2024年02月04日
    浏览(55)
  • flask启动为什么会调用__call__

    先来看看__call__什么时候时候可以调用 不返回任何结果 有结果返回 注意这个__call__必须要实例化之后才可以调用不然不会调用 无任何结果返回 内置函数同样可以用__call__()方法来调用 自定义函数也也可以通过__call__()方法来调用 接下来理解为什么 这个最后会调取Flask的__cal

    2024年02月16日
    浏览(28)
  • 0062__对象指针为NULL,为什么还是可以调用成员函数

    对象指针为NULL,为什么还是可以调用成员函数_空对象指针为什么能调用函数_一颗石头崽儿的博客-CSDN博客

    2024年02月10日
    浏览(46)
  • 【C++】 为什么多继承子类重写的父类的虚函数地址不同?『 多态调用汇编剖析』

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 本篇文章主要是为了解答有关多态的那篇文章那块的一个奇怪现象,大家还记得这张

    2024年02月19日
    浏览(38)
  • 已经安装了 python 库,但是为什么调用不了;ModuleNotFoundError: No module named ‘xxx库名‘

    ModuleNotFoundError: No module named ‘autox’ 但是通过 pip list 查看是可以发现这个库的!!!! 最近在尝试一些 自动机器学习方面的内容Auto ML,于是就装了一下 autox 这个库。将 github 上的仓库克隆下来之后,进入该仓库的主文件目录,使用 下面的指令进行安装。 通过 pip list 查看

    2024年02月12日
    浏览(49)
  • 面试官:Feign 第一次调用为什么会很慢?大部分人都答不上来!

    作者:Lxlxxx 链接:https://juejin.cn/post/7249624466150408250 首先要了解Feign是如何进行远程调用的,这里面包括,注册中心、负载均衡、FeignClient之间的关系,微服务通过不论是eureka、nacos也好注册到服务端,Feign是靠Ribbon做负载的,而Ribbon需要拿到注册中心的服务列表,将服务进行负

    2024年02月12日
    浏览(45)
  • Android基础到进阶UI祖父级 ViewGroup介绍+实用

    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果是warp_content情况下,记录宽和高 int width = 0; int height = 0; //记录每一行的宽度,width不断取最大宽度 int lineWidth = 0; //每一行的高度,累加至h

    2024年04月16日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包