首发公众号:Android程序员日记html
做者:贤榆的榆android
若是喜欢,请 关注 | 赞扬 | 点在看程序员
阅读时间:4978字 8分钟npm
今天恰好是《千与千寻》在中国首映的日子。因此放一张《千与千寻》的海报,我也没想到过去这么久了,其实都都已经看过不少遍了。但仍是想要在大屏幕上再看一次。小时候看动画片,妈妈会说,这动画片有什么好看的,你能看一生呀!真是一语成真,估计这辈子是逃不出动漫的坑了。(杠精就别纠结动画片和动漫的不一样了,在妈妈眼里,那都是同样同样的!),好了,娱乐休闲以前先学习一波儿!canvas
前几天作一个界面的时候,再一次出现了虚线显式成实现的问题,我没出息的又去搜解决方案了,为了记忆深入,狠了狠心,深挖了一下。一块儿来看看吧:bash
我在xml布局文件中经过给一个View设置一个背景画了一条虚线分割线代码以下:cookie
<View
android:id="@+id/view3"
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@drawable/common_line_draw_dash"/>
复制代码
view中引用的common_line_draw_dash.xml
代码以下:app
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<stroke
android:width="1dp"
android:color="#DDDDDD"
android:dashGap="2dp"
android:dashWidth="4dp"/>
</shape>
复制代码
但是设备上的实际效果也预期效果并不一致,见下图: ide
可能不少开发小伙伴都碰到过这个问题,也知道只须要在View的xml布局中添加以下一行代码便可解决问题。 android:layerType="software"
布局
古话说“知其然知其因此然”
从谷歌官方提供的关于硬件加速的资料显示:
从Android 3.0(API级别11)开始,Android 2D渲染管道支持硬件加速,这意味着在View的画布上执行的全部绘图操做都使用GPU。 因为启用硬件加速所需的资源增长,您的应用程序将消耗更多RAM。
而且文章中还讲到Android 4.0(API级别14)开始默认开启了硬件加速;可是
再往上找了一些关于硬件加速的文章好比这篇《关于硬件加速那点事儿》 地址:www.jianshu.com/p/9cd7097a4… 这是一篇Android硬件加速官方文章的译文 里面介绍了不支持硬件加速的一些操做:
在里面看了一下以后就去扫View的源码了,果真不出五分钟就找了上图中倒数第三的方法saveLayer();
看到background.draw(canvas)
方法时,你可能想问我怎么知道这个虚线xml文件,最后实例化以后的Drawble对象对应的GradientDrawble的对象。看下面这张图,你就明白了:
按照日常,文章写道这里就已经结束了,一切都已经说通了。可是做为一名程序员,严谨是一种良好职业操守,因而我又去了google官网看了英文原版关于硬件加速的介绍。 地址:developer.android.com/guide/topic…
既然这样咱们就须要从新去须按照其余的证据来证实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.mStrokeDashWidth
和state.mStrokeDashGap
构造一个DashPathEffect对象,而后在经过Paint的setPathEffect(e)方法来绘制虚线。
到这里一切都水落石出了。之因此虚线会在大部分手机上绘制成实现是由于就是由于Paint的setPathEffect()方法不支持硬件加速。其实经过上表能够看到,在Android 9(API级别28)之后,就能够不用关闭硬件加速也能够绘制XML定义的虚线了。
好了这篇文章就写到这里吧,你们在追源码的时候给你们一个提醒:
晚上10点之后不宜阅读源,由于你根本停不下来。[Facepalm][Facepalm][Facepalm]
虽然有些滑稽,但倒是真是的哈哈。
另外,若是你是初学者,到没有必要花费太多精力去探索。毕竟初学者完成一个App,对知识和技术的广度认知比深度要重要的多。可是到了必定时候(我也说不清是何时,你本身应该会有感受)——我我的以为是作了两三年吧,当你遇到问题时,就不该仅仅只停留在解决问题的层面,还应该深刻了解一下为何。哪怕一开始你就知道致使这个问题的缘由是某个方法。可是找出这个方法,这一探索,研究的过程才是这个阶段你成长的最大助力。与君共勉!
若是有想法能够留言;以为有帮助,还请右下角**「在看」**走一波!
系列文章
- 「Do.006」实战(1)——我想说“开始吧”
- 「Do.007」实战(2)——使用Github进行版本管理
- 「Do.008」实战((3)——Git 分支管理模型
- 「Do.009」实战(4)——AndroidStudio插件推荐
- 「Do.014」实战(5)—— gradle 配置release与debug环境分离
其余