Android ViewGroup onDraw为什么没调用

ViewGroup,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用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(Canvas canvas, ViewGroup parent, long drawingTime)方法...// Fast path for layouts with no backgroundsif ((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 backgroundsif ((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)的方法如下:

    public void draw(Canvas canvas) {//省略boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;if (!verticalEdges && !horizontalEdges) {// 绘制自身内容onDraw(canvas);// 绘制子viewdispatchDraw(canvas);//省略// we're done...return;}//省略}

viewGroup和View初始化时对于PFLAG_SKIP_DRAW标记做了不同的处理。
在这里插入图片描述
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的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28690.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android APP外包开发的三种方式

开发android APP有三种方式,分别是原生开发、混合开发和无代码开发,原生开发对开发者有一定要求,但用户体验好;混合开发是使用H5开发,对开发者要求相对较低;而无代码开发则是通过操作界面搭建APP&#xff0…

(数组与矩阵) 剑指 Offer 50. 第一个只出现一次的字符 ——【Leetcode每日一题】

❓ 剑指 Offer 50. 第一个只出现一次的字符 难度:简单 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。 示例 1: 输入:s “abaccdeff” 输出:‘b’ 示例 2: 输入:s “”…

Linux之vi命令

vi编辑器 vim/vi是Unix / Linux上最常用的文本编辑器而且功能非常强大。 只有命令,没有菜单。 建议使用vim命令,如果没有这个命令可以使用 yum install -y vim 进行安装 命令模式:又称一般模式 编辑模式:又称底行模式,…

Docker本地镜像发布到私有库

Docker Registry(Docker镜像仓库) 使用Docker Registry,可以创建私有或公共的镜像仓库,以存储Docker镜像。私有仓库可以用于存储公司内部的镜像,或者用于个人项目的镜像。公共仓库则会将发布的镜像分享到全世界。 1 …

P22-p26

p22光照渲染,自动曝光,雾 如果屋子里黑,可以在世界大纲搜索light把平行光和天光改变为可移动,屋子里就亮了(如果屋子内还没亮就重新再构件一次光照) 1,虚幻引擎自带光源 定向光源一般模拟太阳…

Ubuntu 更改内核启动顺序

ubuntu服务器系统中用run包安装了某卡的驱动,后来又安装了docker,重启后,驱动失效。 经分析 安装docker时,又把新的linux内核安装上了。驱动是安装在旧内核上。 然会重新安装驱动,失败,确认是因为驱动只支…

Ansible Playbook剧本配置文件

一、执行文件 Playbook配置文件使用YAML语法,具有简 洁明了,结构清晰等特点。Playbook配置文件类似于shell脚本,是一个YAML格式的文件,用于保存针对特定需求的任务列表,前面介绍的ansible命令虽然可以完成各种任务&a…

大语言模型:从应用到产出;百度网盘推出AI的图搜功能

🦉 AI新闻 🚀 百度网盘推出基于AI的高级图搜功能,提供更精确、全面的搜索结果 摘要:百度网盘近日推出了一项名为“高级图搜”的AI功能。通过基于向量的语义搜索,该功能可以理解时间、地点、人物、事件等组合搜索语句…

【基于 GitLab 的 CI/CD 实践】03、GitLab Pipeline 实践(上)

目录 一、GitLab Pipeline 流水线语法有哪些?流水线参数列表 如何检查语法错误?流水线语法检测 二、Pipeline 基础语法 job script before_script after_script stages 未定义 stages ​定义 stages 控制 stage 运行顺序 .pre & .post …

华为产品测评官-开发者之声 - 真实体验感想

华为产品测评官-开发者之声 - 真实体验感想 我先是在6月17日参加了华为在深圳举办的开发者大会,后面看到群里发的"2023华为产品测评官-开发者之声"活动,简单看了一下体验活动,感觉好玩又能学到东西&#xf…

【LLM】Langchain使用[四](评估、代理)

note 评估目的:检验LLM是否达到验收标准;分析改动对于LLM应用性能的影响 思路:利用语言模型和链,辅助评估 代理: 代理能方便地将LLM连接自己的信息来源(数据)、API等PythonREPLTool工具&#x…

20世纪的传奇Delphi,退出历史舞台?突破传统开发,该用新工具了

先来一波回忆杀吧 有人知道Delphi吗? Delphi的历史可以追溯到1995年,当时它由Borland公司推出。 最初的版本被称为Delphi 1,它是一个基于Object Pascal语言的可视化开发环境,用于创建Windows应用程序。 随着时间的推移&#xff…