前段時間因爲新冠肺炎特別嚴重,政府規定今年的4月4號爲悼念日,全部互聯網項目能置灰的要跟隨置灰處理。咱們能夠看到在京東、百度等部分app中都有置灰的功能。若是是在網頁上的話,只須要一句代碼就能夠搞定了,可是app裏實現可能有些同窗會感受迷茫。今天筆者也跟上潮流,給你們分享一篇如何在app中實現全局置灰吧,沒有這個需求的朋友們也能夠學習探討一下思路,但願能夠幫到你們!程序員
實現灰度化的思路應該從Paint出發,由於系統是經過Paint將內容繪製到界面上的,若是能找到Paint相關的設置方法,那就再也合適不過了。自定義View作得多的同窗可能知道Paint中能夠設置ColorMatrix,如下是其源碼,從源碼的註釋咱們能夠看到若是將setSaturation的sat參數設置爲0就表明灰度模式。app
OK,說幹就幹,咱們自定義一個圖片控件,修改其Paint中的Matrix屬性試試。首先初始化ColorMatrix對象,而後按照源碼中的說法,將setSaturation參數的值設置爲0,接下來將該ColorMatrix設置到Paint中,最後再onDraw方法中使用剛剛的Paint對象,代碼大體以下:佈局
以上是在ImageView中實現灰度化,那麼文本的TextView其實也是同樣的,由於本質都是使用Paint進行的繪製,因此能夠直接將上面代碼拷貝到自定義TextView中。post
接下來咱們在頁面中使用上面自定義好的2個View,咱們運行一下,看一下效果。不出因此然,文本和圖片都變成了灰色,具體運行結果以下圖:學習
效果是實現了,又有一個新的問題擺在眼前。在項目裏數以萬計的ImageView和TextView,總不可能一個一個來替換吧,這樣作要換到猴年馬月去?spa
從上面TextView、ImageView兩者的置灰實現沒有任何區別,咱們能夠猜想是否是全部的View都能給置灰呢,那麼ViewGroup做爲一個特殊的View是否能夠置灰呢?接下來咱們來驗證一下這個猜測,自定義一個RelativeLayout,仍是以前的代碼,須要注意的是這裏須要複寫dispatchDraw方法。關於onDraw方法和dispatchDraw方法的差異這裏也稍微解釋一下,ViewGroup容器組件的繪製,當它沒有背景時直接調用的是dispatchDraw方法, 而繞過了onDraw方法,當它有背景的時候就調用onDraw方法,而onDraw()方法裏包含了dispatchDraw方法的調用。也就是說這裏必須複寫dispathDraw方法,不然將沒效果。插件
定義好ViewGroup之後,接下來將它使用到頁面的根佈局上面,運行項目看結果:3d
咱們看到頁面確實也成功灰度化了,心中暗暗竊喜,終於能夠減小不少工做量了。然而問題仍是有的,項目中有幾百個頁面,每一個頁面ViewGroup又不同,我難道要先準備GrayLinearLayout、GrayRelativeLayout、GrayFramelayout等等,而後一個一個去換根佈局嗎,想一想都有點累,那有沒有更加優雅的方式呢?cdn
通常來講,最好用的方案每每來源於源碼中,這也是資深程序員常常查看源碼的緣由。帶着這個疑問咱們先來簡單複習一下View的加載過程,在我以前分享插件化的時候有簡單提到過。咱們從setContentView方法中一路點下去,最終會看到調用了LayoutInflater類中的createViewFromTag方法,這段調用過程很簡單,沒看過的同窗能夠親自去查看一下。筆者點了不少次了,這裏就直接貼出來相關代碼吧,重點是紅框裏面的部分。xml
在紅框裏是具體的生成View過程,這裏分爲3種狀況,優先Factory2,其次是Factory,最後是默認的onCreateView方法。關於這裏爲何會有3種狀況,是由於歷史緣由,爲了兼容AppCompact。默認狀況下,前兩個都是爲空的,會直接進入onCreateView方法。那麼思路來了,咱們能夠能夠在加載View的時候,經過替換BaseActivity的佈局統一替換全部頁面呢?
在Activity的onCreate方法中調用如下代碼,hook住系統加載View的流程,根據前面的源碼,若是設置了factory將進入自定義的onCreateView方法而再也不進入系統的onCreateView方法。看過AMS或者事件分發流程的同窗確定知道,在咱們本身寫的xml根佈局之上還有一個系統的FrameLayout,關於這一塊不太懂的同窗能夠去看我以前的事件分發文章,裏面有詳細講到。這裏咱們就利用這一點,找到這個id是"content"的系統FrameLayout,而後將該FrameLayout替換成咱們本身的帶有置灰代碼的新FrameLayout便可。
經過前面的源碼得知,在不設置Factory的狀況下,將調用到系統的onCreateView方法,因此咱們也能夠直接在Activity的onCreateView方法中加上以上代碼,效果是同樣的。
這兩種方式差異不大,都是能夠用的,這裏建議使用第二種方式,儘可能走系統自身的回調。
本次咱們從app全局置灰怎麼來實現這一話題,漸漸深刻,不斷探索更優雅的實現方式。將一個ImageView置灰的猜測,引伸出了十幾行代碼就置換了項目中全部的View,本質也是經過查看源碼的方式,一步一步地套出了方案。本文方案參考自鴻洋大神的技術文章,筆者在此基礎上進行了一些拓展和源碼解釋,這裏也必須感謝一下大神們的默默付出,站在巨人的肩膀上感受真心不錯。看完之後對內容有疑問或者有改進建議的同窗,歡迎一塊兒探討學習,共同進步!
本文參考自原文:juejin.im/post/5e8893…