资讯专栏INFORMATION COLUMN

「Do.023」为啥用XML定义的虚线显示成了实线

William_Sang / 2178人阅读

摘要:由于启用硬件加速所需的资源增加,您的应用程序将消耗更多。之所以虚线会在大部分手机上绘制成实现是因为就是因为的方法不支持硬件加速。其实通过上表可以看到,在级别以后,就可以不用关闭硬件加速也可以绘制定义的虚线了。

首发公众号:Android程序员日记
作者:贤榆的榆
如果喜欢,请 关注 | 赞赏 | 点在看
阅读时间:4978字 8分钟
前言

今天刚好是《千与千寻》在中国首映的日子。所以放一张《千与千寻》的海报,我也没想到过去这么久了,其实都都已经看过很多遍了。但还是想要在大屏幕上再看一次。小时候看动画片,妈妈会说,这动画片有什么好看的,你能看一辈子呀!真是一语成真,估计这辈子是逃不出动漫的坑了。(杠精就别纠结动画片和动漫的不同了,在妈妈眼里,那都是一样一样的!),好了,娱乐休闲之前先学习一波儿!

正文

前几天做一个界面的时候,再一次出现了虚线显式成实现的问题,我没出息的又去搜解决方案了,为了记忆深刻,狠了狠心,深挖了一下。一起来看看吧:

我在xml布局文件中通过给一个View设置一个背景画了一条虚线分割线代码如下:

view中引用的common_line_draw_dash.xml代码如下:



    

可是设备上的实际效果也预期效果并不一致,见下图:

可能很多开发小伙伴都碰到过这个问题,也知道只需要在View的xml布局中添加如下一行代码即可解决问题。
android:layerType="software"

古话说“知其然知其所以然”

从谷歌官方提供的关于硬件加速的资料显示:

从Android 3.0(API级别11)开始,Android 2D渲染管道支持硬件加速,这意味着在View的画布上执行的所有绘图操作都使用GPU。 由于启用硬件加速所需的资源增加,您的应用程序将消耗更多RAM。

并且文章中还讲到Android 4.0(API级别14)开始默认开启了硬件加速;但是

再往上找了一些关于硬件加速的文章比如这篇《关于硬件加速那点事儿》
地址:https://www.jianshu.com/p/9cd...
这是一篇Android硬件加速官方文章的译文
里面介绍了不支持硬件加速的一些操作:

在里面看了一下之后就去扫View的源码了,果然不出五分钟就找了上图中倒数第三的方法saveLayer();

看到background.draw(canvas)方法时,你可能想问我怎么知道这个虚线xml文件,最后实例化之后的Drawble对象对应的GradientDrawble的对象。看下面这张图,你就明白了:

按照平常,文章写道这里就已经结束了,一切都已经说通了。但是作为一名程序员,严谨是一种良好职业操守,于是我又去了google官网看了英文原版关于硬件加速的介绍。
地址:https://developer.android.com...

然后这一看,就看出问题了。当我看到上面这张图的时候,我特么就尴尬了。怎么和之前我看到的那张图不一样了?saveLayer方法支持硬件加速?还是不支持?后来又用google搜索了一些资料,确实没有一项有力的证件证明saveLayer方法是不支持硬件加速的;

既然这样我们就需要重新去须按照其他的证据来证明View在画虚线时,无法使用硬件加速,虽然上面图中找到的saveLayer方法不能证明是需要关闭硬件加速的原因。但是这个View绘制背景的流程是没有问题的,并且我们知道用标签定义的xml文件在膨胀后得到的是一个GradientDrawable对象。

既然在绘制背景流程中没有可疑的不支持硬件加速的方法,那么在生成Drawable对象的过程是否能找得到一些可疑的方法呢?上面流程图中的mBackground对象又是怎么来的?

带着这两个问题,思考了一下。这个View是通过xml膨胀生成的,那么应该会调用View的2各参数或3个参数的构造方法,然后我就顺着这思路找了一下mBackground对象是怎么生成:

一路找下去找到了,上图中蓝色框部分的代码

final Drawable.ConstantState cs;
if (isColorDrawable) {
    cs = sPreloadedColorDrawables.get(key);
} else {
    cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}
Drawable dr;
boolean needsNewDrawableAfterCache = false;
if (cs != null) {
    if (TRACE_FOR_DETAILED_PRELOAD) {
        // Log only framework resources
        if (((id >>> 24) == 0x1) && (android.os.Process.myUid() != 0)) {
            final String name = getResourceName(id);
            if (name != null) {
                Log.d(TAG_PRELOAD, "Hit preloaded FW drawable #"
                        + Integer.toHexString(id) + " " + name);
            }
        }
    }
    dr = cs.newDrawable(wrapper);
} else if (isColorDrawable) {
    dr = new ColorDrawable(value.data);
} else {
    dr = loadDrawableForCookie(wrapper, value, id, density, null);
}

然后大概可以看明白当cs不为null时,Drawable是通过cs.newDrawable()方法生成的。cs在上面代码中的第一行已经定义了,它是一个Drawable的静态内部类Drawable.ConstantState;

既然前面我们已经知道用标签定义的虚线XML文件膨胀后是一个GradientDrawable对象。那么GradientDrawable也应该有自己的ConstantState内部类。点进GradientDrawable的源码拉到底部,果然就看到了一个继承自ConstantState的GradientState类。借着又是一招顺藤摸瓜:

GradientState类重写了ConstantState的newDrawable方法,在该方法中通过调用GradientDrawable的构造方法构建了一个GradientDrawable实例。

在GradientDrawable构造方法中则进行了一些初始动作,其中调用的updateLocalState方法中的一段代码(上图蓝色框)引起了我注意,最引人注目的还是那段红色框里的的方法。这个方法好像在不支持硬件加速的操作表中。喜极而泣!明了,明了...。

这里再粘一下这段代码:

if (state.mStrokeDashWidth != 0.0f) {
    final DashPathEffect e = new DashPathEffect(
            new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0);
    mStrokePaint.setPathEffect(e);
}

它判断了StrokeDashWidth是否有值,如果有这则根据虚线的的两个重要属性state.mStrokeDashWidthstate.mStrokeDashGap构造一个DashPathEffect对象,然后在通过Paint的setPathEffect(e)方法来绘制虚线。

到这里一切都水落石出了。之所以虚线会在大部分手机上绘制成实现是因为就是因为Paint的setPathEffect()方法不支持硬件加速。其实通过上表可以看到,在Android 9(API级别28)以后,就可以不用关闭硬件加速也可以绘制XML定义的虚线了。

后记

好了这篇文章就写到这里吧,大家在追源码的时候给大家一个提醒:

晚上10点以后不宜阅读源,因为你根本停不下来。

虽然有些滑稽,但却是真是的哈哈。

另外,如果你是初学者,到没有必要花费太多精力去探索。毕竟初学者完成一个App,对知识和技术的广度认知比深度要重要的多。但是到了一定时候(我也说不清是什么时候,你自己应该会有感觉)——我个人觉得是做了两三年吧,当你遇到问题时,就不应仅仅只停留在解决问题的层面,还应该深入了解一下为什么。哪怕一开始你就知道导致这个问题的原因是某个方法。但是找出这个方法,这一探索,研究的过程才是这个阶段你成长的最大助力。与君共勉!

如果有想法可以留言;觉得有帮助,可以关注我的公众号

推荐阅读

系列文章

「Do.006」实战(1)——我想说“开始吧”

「Do.007」实战(2)——使用Github进行版本管理

「Do.008」实战((3)——Git 分支管理模型

「Do.009」实战(4)——AndroidStudio插件推荐

「Do.014」实战(5)—— gradle 配置release与debug环境分离

其他

「Do.016」图解Win电脑下载腾讯视频转mp4

「Do.017」如何高效使用Win电脑?

「Do.018」接私活儿,是否有必要?

「Do.019」2018这一年——年终总结

「Do.020」程序员该如何在寒冬中自处

「Do.021」一文了解AndroidStudio3.4的全部更新

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75026.html

相关文章

  • 面试官: "css实现android系统loading动画"

    摘要:源码常用的图标有种一种是的菊花一种是的环今天我们用实现的环动画下节课实现的菊花注意帧数少的原因实际动画效果是很平滑的首先我们定义的画布尺寸为在浏览器中缩放为显示这个你可以根据实际需要调整定义环的圆心坐标为半径为算下周长大概为后面会用 源码: https://github.com/any86/any-... ios/android web常用的loading图标有2种, 一种是ios的菊...

    caige 评论0 收藏0
  • 设计模式之UML类图

    摘要:因为依赖关系不强制,所以用虚线表示关联关系关联关系是类属性依赖,很关键,所以使用实线表示。源码地址类图参考慕课网设计模式精讲大话设计模式设计模式之类图学习二类图 类图(Class diagram)主要用于描述系统的结构化设计。类图也是最常用的UML图,用类图可以显示出类、接口以及它们之间的静态结构和关系。 0x01.类图中的元素 1.类 Class / 接口 Interface sho...

    xiao7cn 评论0 收藏0
  • canvas核心技术-如何绘制线段

    摘要:,决定了两条线段相交时如何绘制焦点,只有当两条线段方向不同时,才会生效。,告诉浏览器如何绘制形式的线段焦点,只有当有效,默认为。 这篇是学习和回顾canvas系列笔记的第一篇,完整笔记详见:canvas核心技术 学习canvas,首先得知道如何去绘制线段,然后才能通过很多简单的线段去实现比较复杂的图形,比如常见的图表,柱状图,折线图等都是通过一段一段的线段实现的。 基础知识 canva...

    pubdreamcc 评论0 收藏0
  • 【自定义View】洋葱数学同款雷达图深入解析-RadarView

    摘要:开箱即用的源码地址洋葱数学同款雷达图支持自定义属性雷达网的半径该属性决定了的宽高各属性表示的最大进度雷达网的颜色雷达网的线宽各属性文字的颜色各属性文字和中心处名字的字体路径中心连接区域的颜色中心连接区域的边框颜色中心处的名字中showImg(https://user-gold-cdn.xitu.io/2019/5/13/16ab091f638c526a); showImg(https://u...

    番茄西红柿 评论0 收藏0
  • 【自定义View】洋葱数学同款雷达图深入解析-RadarView

    摘要:开箱即用的源码地址洋葱数学同款雷达图支持自定义属性雷达网的半径该属性决定了的宽高各属性表示的最大进度雷达网的颜色雷达网的线宽各属性文字的颜色各属性文字和中心处名字的字体路径中心连接区域的颜色中心连接区域的边框颜色中心处的名字中showImg(https://user-gold-cdn.xitu.io/2019/5/13/16ab091f638c526a); showImg(https://u...

    Flands 评论0 收藏0

发表评论

0条评论

William_Sang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<