今天是端午節(jié)叉庐,祝大家節(jié)日快樂
這篇文章著實醞釀了許久宏多,一直懶得寫。網(wǎng)上關(guān)于通知欄樣式適配的文章很多嗦随,但還不夠完美列荔,這也是我寫這篇文章的動力所在。
溫故而知新
Android通知有兩種枚尼,默認通知與自定義通知贴浙。默認通知簡單調(diào)用系統(tǒng)接口就能實現(xiàn),如下:
自定義通知就稍微麻煩一些署恍,需要定義一個layout文件崎溃,使用RemoteViews加載它并設(shè)置一些點擊事件,再設(shè)置到builder盯质,如下:
這個通知很簡單袁串,就是兩行文本加上一個按鈕概而,按鈕具有單獨的點擊事件,點擊后跳轉(zhuǎn)到AnotherActivity囱修。
注意:smallIcon對于自定義通知和默認通知都是必須的赎瑰,否則通知顯示不出來。道理很簡單蔚袍,smallIcon需要在狀態(tài)欄上顯示乡范,不設(shè)置怎么行。在5.0及以上啤咽,smallIcon必須符合Material Design風(fēng)格晋辆,即白色內(nèi)容,透明背景宇整。不然系統(tǒng)會使用默認的圖片替換瓶佳。具體可參考Android通知欄微技巧,那些你所沒關(guān)注過的小細節(jié) 標簽: android通知通知欄微技巧鳞青。后面我會有一篇更詳細的文章來介紹這個霸饲。contentIntent對于2.3及以下的系統(tǒng)是必須的,否則發(fā)送通知時會拋異常臂拓。道理也很簡單厚脉,Android 2.3及以下系統(tǒng)不支持給自定義通知上的元素綁定單獨的點擊事件,因此必須設(shè)置整個通知的點擊事件胶惰。
為什么要進行樣式適配傻工?
默認通知不存在樣式適配的問題,因為默認通知的布局孵滞、顏色中捆、背景什么的都是系統(tǒng)的,系統(tǒng)總會正確的顯示默認通知坊饶。但自定義通知就不一樣了泄伪,自定義通知的布局完全由我們自己掌控,我們可以為元素設(shè)置任何背景匿级、顏色蟋滴。那么,問題來了根蟹。Android通知欄的背景各種各樣脓杉,不同的ROM有不同的背景,白色简逮、黑色球散、透明等。不同的Android版本通知欄背景也不一樣散庶,一旦我們?yōu)樽远x通知上的元素設(shè)置了特定背景或顏色蕉堰,就肯定會帶來兼容性問題(主要是文本啦)凌净。這樣的應(yīng)用一大把,貼個圖大家就明白了:
怎么適配屋讶?
適配的方式大概有兩種冰寻,一種簡單粗暴:為自定義通知設(shè)置固定的背景(上圖中的360衛(wèi)士就這么干的),比如黑色皿渗。那么內(nèi)容自然就是白色或近似白色斩芭。這樣,在所有的手機上都能正常顯示乐疆,不會出現(xiàn)在黑色背景通知欄上顯示良好划乖,到了白色背景通知欄上就幾乎啥也看不見。使用這種方案的應(yīng)用太多了挤土。我個人很不推崇這種方式琴庵,這樣會使得自定義通知在將近一半的手機上顯示得很突兀,和系統(tǒng)的通知欄不夠沉浸仰美,影響整體美觀迷殿。另一種方案就稍微合理一些:通過讀取系統(tǒng)的通知欄樣式文件,獲取到title和content的顏色咖杂,進而將這個顏色設(shè)置到自定義通知上庆寺。讀取通知欄樣式文件本身有兼容性問題,不同Android版本的樣式文件有變诉字,具體可參考這篇博客 通知欄設(shè)置系統(tǒng)字體顏色 止邮,這種方式也不是在所有手機上生效,實際測試發(fā)現(xiàn)奏窑,還是有小部分機型沒法讀取或是讀取到的是錯誤的。拿到title和content的顏色后屈扎,還可以通過算法(后面細說)判斷這個顏色是近似白色還是近似黑色埃唯,進而能判斷出通知欄的背景是近似黑色還是近似白色,這樣就能根據(jù)不同的通知欄背景加載不同的自定義通知布局鹰晨。進而做到良好的適配墨叛。
更好的適配
現(xiàn)在切入主題,談?wù)勅绾蝸砀玫倪m配自定義通知模蜡。有過鎖屏開發(fā)經(jīng)驗的人應(yīng)該知道漠趁,如果你的應(yīng)用有讀取系統(tǒng)通知欄的權(quán)限,那么每當(dāng)應(yīng)用程序發(fā)出一個通知忍疾,你的應(yīng)用都會收到對應(yīng)的notification對象闯传,這個時候,我們一般會執(zhí)行以下操作:
調(diào)用addView之后卤妒,應(yīng)用程序的通知就會顯示在我們的應(yīng)用里甥绿。顯然字币,上面的代碼并沒有對apply返回的notificationItemLayout做任何其他操作,但確實這個View顯示出來時就是樣式良好的共缕,可見洗出,notificationItemLayout本身就是帶有樣式的,即便是默認通知图谷。那么方案來了翩活!我們先構(gòu)造一個默認通知:
通知并不發(fā)送出去,只是用來獲取通知欄title的顏色便贵,如果你還想獲取content的顏色菠镇,抱歉,不能通過查找android.R.id.text來獲取嫉沽,這個字段是訪問不到的辟犀。可通過反射獲取绸硕,更好的辦法是先預(yù)先設(shè)置一個content堂竟,然后遍歷viewGoup根據(jù)content內(nèi)容找到對應(yīng)的TextView再獲取顏色。
拿到顏色后玻佩,可根據(jù)算法判斷這個顏色是近似白色還是近似黑色出嘹,我們使用黑色作為基準色,使用方差來計算這個顏色是否近似黑色:
baseColor傳入Color.BLACK咬崔,color傳入剛剛獲取到的title的顏色税稼,根據(jù)我實測,閾值為180.0較為合理垮斯。上述方法返回true郎仆,即表示title的顏色近似黑色,也就是說通知欄背景近似白色兜蠕。
額扰肌,經(jīng)驗豐富的同學(xué)應(yīng)該已經(jīng)洞察到第二段代碼存在的兼容性問題了:根據(jù)android.R.id.title去找到title對應(yīng)的TextView是不靠譜的,因為有些ROM廠商會把id改掉熊杨,導(dǎo)致找到的title為空曙旭。
同時還有另外一個問題:使用上述方法,Activity不能繼承自AppCompatActivity(實測5.0以下機型可以晶府,5.0及以上機型不行)桂躏,大致的原因是默認通知布局文件中的ImageView(largeIcon和smallIcon)被替換成了AppCompatImageView,而在5.0及以上系統(tǒng)中川陆,AppCompatImageView的setBackgroundResource(int)未被標記為RemotableViewMethod剂习,導(dǎo)致apply時拋異常。
為了解決這兩個問題,我們改進getNotificationColor方法:
在getNotificationColorInternal中进倍,設(shè)置一個默認的title文本土至,如果根據(jù)id找不到title,則遍歷notificationRoot根據(jù)設(shè)置的title文本找到title:
在getNotificationColorCompat中猾昆,我們先構(gòu)造一個默認通知陶因,獲取到默認通知的布局文件id,并將布局加載到notificationRoot垂蜗,此時楷扬,如果根據(jù)id找不到title,顯然設(shè)置默認title的辦法已經(jīng)失效了贴见。如何從notificationRoot中找到title是個問題烘苹。我的解決辦法是:反正都已經(jīng)拿到notificationRoot了,不如就遍歷它片部,先找到其中的所有TextView镣衡,取字體最大的TextView作為title(這是合理的,因為默認通知中最多也就4個TextView档悠,分別是title廊鸥、content、info辖所、when惰说,title肯定是字體最大,最顯眼的)缘回,并返回其顏色:
實際測試
拿到了通知欄背景的顏色后吆视,我們就可以加載不同樣式的布局,達到適配的目的酥宴。代碼如下:
效果:
好了啦吧,我的第一篇技術(shù)博客到此為止,大家假期玩得Happy拙寡!