首發公衆號: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…
而後這一看,就看出問題了。當我看到上面這張圖的時候,我特麼就尷尬了。怎麼和以前我看到的那張圖不同了?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.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環境分離
其餘