为什么ViewGroup的onDraw()方法不执行

2023-11-02

问题

ViewGroup onDraw不执行的原因?
怎么让ViewGroup onDraw执行?

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

原因详解

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

在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(x1,x2,x3)方法
                ...
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    dispatchDraw(canvas);
                } else {
                    draw(canvas);
                }
                ...

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

  1. 注释: ViewGroup.drawChild()调用此方法,使每个子视图都绘制自己。这是视图根据图层类型专门处理渲染行为的地方,硬件加速
  2. 是否走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,也分为是否支持硬件加速,因此绘制流程也有所差异,流程图简示如下:

从上图可以看出,不管是否开启硬件加速,都会经历“跳过绘制”的逻辑判断,而该判断的分支就决定了viewGroup的ondraw()方法是否执行。如果“跳过绘制”成立,那么调用dispatchDraw()方法,继而调用子view进行绘制(如果有子view)。如果“跳过绘制”不成立,那么调用draw(x1),该方法会调用dispatchDraw()和ondraw()方法。也就是说无论怎样 dispatchDraw() 是一定会调的

讲解到这里,我们大家应该清楚,硬件加速这个知识点只是一个小插曲,最重要的是 mPrivateFlags & PFLAG_SKIP_DRAW 是在哪里被赋值的?从而影响到draw()方法的调用?

其实很简单,我一说大家就能明白,这种变量的初始化肯定在构造函数里写啊!!!

对,你们已经明白了,就是在ViewGroup里调用了setFlag(参数1,参数2),对 mPrivateFlags & PFLAG_SKIP_DRAW 这两个参数进行了设置

其实官方的注释 // ViewGroup doesn't draw by default 已经说明了一切,不过,我们还是来看看 setFlag 的源码

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()方法没有执行的原因:viewGroup默认设置了WILL_NOT_DRAW标记,进而设置了PFLAG_SKIP_DRAW标记,而在绘制的时候通过判断PFLAG_SKIP_DRAW标记来决定是否调用ViewGroup draw(x)方法,最终决定是否调用onDraw()方法。而view默认没有设置WILL_NOT_DRAW标记,也就没有后面的事了。

如何让viewGroup onDraw()执行

既然知道了ViewGroup没有绘制的原因,那么就有方法让它执行绘制流程。先来看看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),可以在  继承ViewGroup的自定义View  里使用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;
            }
      }
    }

总结

  1. 若要ViewGroup onDraw()执行,只需要setWillNotDraw(false)、设置背景、设置前景、设置焦点高亮,4个选项其中一项满足即可。
  2. 当然如果不想在 继承ViewGroup的自定义View onDraw里绘制,也可以重写ViewGroup dispatchDraw()方法,在该方法里绘制 自定义View 内容。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么ViewGroup的onDraw()方法不执行 的相关文章

  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • 在 SQLite 中搜索时排除 HTML 标签和一些 UNICODE 字符

    更新 4 我已经成功运行了firstchar例如 但现在的问题是使用regex 即使包含头文件 它也无法识别regex操作员 有什么线索可以解决这个问题吗 更新 2 我已经编译了sqlite3我的项目中的库 我现在正在寻找任何人帮助我为我的
  • 如何使用InputConnectionWrapper?

    我有一个EditText 现在我想获取用户对此所做的所有更改EditText并在手动将它们插入之前使用它们EditText 我不希望用户直接更改中的文本EditText 这只能由我的代码完成 例如通过使用replace or setText
  • 如何默认在 ActionOpenDocument 意图中显示“内部存储”选项

    我需要用户选择一个自定义文件类型的文件 并将其从 Windows 文件资源管理器拖到 Android 设备上 但默认情况下内部存储选项不可用 当我使用以下命令启动意图时 var libraryIntent new Intent Intent
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • Android Studio - Windows 7 上的 Android SDK 问题

    我对 Google i o 2013 上发布的最新开发工具 Android Studio 有疑问 我已经成功安装了该程序并且能够正常启动 我可以导入现有项目并对其进行编辑 但是 当我尝试单击 SDK 管理器图标或 AVD 管理器图标时 或者
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 我的设备突然没有显示在“Android 设备选择器”中

    我正在使用我的三星 Galaxy3 设备来测试过去两个月的应用程序 它运行良好 但从今天早上开始 当我将设备连接到系统时 它突然没有显示在 Android 设备选择器 窗口中 我检查过 USB 调试模式仅在我的设备中处于选中状态 谁能猜出问
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • Android 套接字和 asynctask

    我即将开始制作一个应该充当 tcp 聊天客户端的应用程序 我一直在阅读和阅读 我得出的结论是最好 如果不需要 将我的套接字和异步任务中的阅读器 问题是我不确定从哪里开始 因为我是 Android 新手 这至少对我来说是一项艰巨的任务 但据我
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • 将两个文本视图并排放置在布局中

    我有两个文本视图 需要在布局中并排放置 并且必须遵守两条规则 Textview2 始终需要完整显示 如果布局中没有足够的空间 则必须裁剪 Textview1 例子 文本视图1 文本视图2 Teeeeeeeeeeeeeeeeeextview1
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview

随机推荐

  • 模拟弱网测试方法总结

    我们测试某些需求 可能需要模拟弱网环境 下面介绍几种模拟弱网的方法 一 使用Fiddler 安装Fiddler 保证手机设备 笔记本IP都在同一个网段 Fiddler中在Rules Custom Rules中设置弱网的标准上传及下传10KB
  • Pandas是用于数据操作和分析的强大库

    Python中有许多流行的统计分析库 下面是其中一些主要的库及其主要用法 NumPy 用途 NumPy是Python中的数值计算库 提供多维数组对象和各种数学函数 用于高效处理大规模数据和执行数值计算 主要用法 创建和操作多维数组 执行数值
  • 比较好用的图床分享

    链接 https picx xpoet cn upload 网页上有教程 实用性强 转存很快 推荐
  • 2022版Web面试上岸手册,最新最细致!

    大裁员背景下 没什么比辞职后找不到工作更扎心 在行情好转前 前端程序员只能 猥琐发育 不轻易跳槽 同时要修炼内功 对八股文 底层源码 重点项目等进行查缺补漏 静待行情好转抓住机会 为帮大家在 就业寒冬 期更好的稳步提升 精进技术 以便保全自
  • MySQL中的日志

    查询日志 binlog redo log undo log介绍 目录 日志 MySQL中的4种日志 错误日志 查询日志和慢查询日志 二进制日志 binlog InnoDB 存储引擎的日志 重做日志 redo log 回滚日志 undo lo
  • clickhouse修改默认密码

    1 明文密码 vim etc clickhouse server users xml 找到下面的语句 增加明文密码
  • Centos7 配置Java开发(JDK)环境

    1 下载Java安装包 在Oracale Java 官网找到对应的JDK安装包 现在最常用 JDK1 8 而且现在的系统大都是64位 这里就以LInux64位为例 2 上传到Centos虚拟机 云主机中 将下载好的jdk for linux
  • Java入门(7)——循环和debug 调试

    循环 while 循环 格式 int i 0 初始化条件 while i lt 10 判断条件 System out println i 循环体 i 控制条件 执行顺序 第一次 第二次 第三次 最后一次 条件满
  • Vue2(路由)

    目录 一 路由原理 hash 二 路由安装和使用 vue2 三 路由跳转 四 路由的传参和取值 五 嵌套路由 六 路由守卫 最后 一 路由原理 hash 单页应用的路由模式有两种 1 哈希模式 利用hashchange 事件监听 url的h
  • Pentaho Data Integration:执行job提示 无法找到作业的开始点

    问题 无法找到作业的开始点 解决办法 如下图 选择 发送邮件 软件下载地址 Pentaho Community Edition Download Hitachi Vantara 找到Pentaho Data Integration Base
  • 在vue项目中 , 将字符串转数组 split()

    console log this cityItem fullName console log this cityItem fullName split 在vue项目中 遇到一个常用的方法split 因为常用 又容易记混 所以在此记录 spl
  • 推荐几个好用的代码工具

    SourceTree git 管理工具 postman 接口调试工具 FinalShell shell 链接工具
  • Salient Obejct Detection(SOD)综述

    论文链接 https arxiv org abs 2008 00230 仓库链接 https github com taozh2017 RGBD SODsurvey 介绍 显著目标检测 Salient Obejct Detection 是模
  • 微服务架构下认证和鉴权理解

    认证和鉴权 从单体应用到微服务架构 优势很多 但是并不是代表着就没有一点缺点了 微服务架构 意味着每个服务都是松散耦合的 因此 作为软件工程师和架构师 我们在分布式架构中面临着安全挑战 微服务对外开放的端点 我们称之为 API 单体应用只需
  • leetcode-62不同路径

    题目 一个机器人位于一个 m x n 网格的左上角 起始点在下图中标记为 Start 机器人每次只能向下或者向右移动一步 机器人试图达到网格的右下角 在下图中标记为 Finish 问总共有多少条不同的路径 示例 输入 m 3 n 2 输出
  • 缩减Docker镜像大小的方法

    对于刚接触容器的人来说 他们很容易被自己构建的 Docker 镜像体积吓到 我只需要一个几 MB 的可执行文件而已 为何镜像的体积会达到 1 GB 以上 本文将会介绍几个奇技淫巧来帮助你精简镜像 同时又不牺牲开发人员和运维人员的操作便利性
  • python的知识点关键词_Python基础知识的集合,知识点,合集

    概念 Python有什么优势 解释性语言 语法简单易懂 可读性强 自动内存管理 基于引用计数法等可以对垃圾进行自动回收 内存池机制 提前申请好小内存 内存分配效率更高 让程序员可以更加专注代码的实现 有很多库可以调用 站在巨人的肩膀上简单的
  • 软件工程导论06-面向数据结构的分析与设计

    面向数据结构的分析与设计 典型方法 Jackon方法 Warnier方法 主要特点 以信息对象及其操作作为核心进行需求分析 认为复合信息对象具有层次结构 并且可按顺序 选择 重复三种结构分解为成员信息对象 提供由层次信息结构映射为程序结构的
  • 在Linux中使用shell指令完成文件打包、压缩、解压缩

    一 写一个1 sh脚本 将以下内容放到脚本中 在家目录下创建目录文件dir 在dir下创建dir1和dir2 把当前目录下的所有文件拷贝到dir1中 把当前目录下的所有脚本文件拷贝到dir2中把dir2打包并压缩为dir2 tar xz 再
  • 为什么ViewGroup的onDraw()方法不执行

    问题 ViewGroup onDraw不执行的原因 怎么让ViewGroup onDraw执行 android代码一直在优化 我看了几个版本的源码 目前 我用的是API30的源码 再去看ViewGroup为什么不走onDraw 的时候 已经