隨手記 Android 沉浸式狀態(tài)欄的踩坑之路
關(guān)于“沉浸式狀態(tài)欄”這種叫法,有的朋友可能會覺得不妥。但是目前網(wǎng)上大部分講到“沉浸式狀態(tài)欄”基本都是指“透明狀態(tài)欄”,所以這里就不討論其對錯了(其實有時候錯的多了,也就成了對的了),大家知道是說的“透明狀態(tài)欄”就行了,下文都是稱這種效果為“沉浸式狀態(tài)欄”。
在Android 4.4之前,所有應用都是無法設置狀態(tài)欄的背景顏色的,都是跟著系統(tǒng)來的(黑色背景狀態(tài)欄),一塊黑色的狀態(tài)欄和應用非常不搭調(diào)。為了提供更好的交互效果,Google在Android 4.4之后提供了設置沉浸式狀態(tài)欄的方法。支持沉浸式狀態(tài)欄的App的界面顯得逼格更高一點,因此隨手記Android客戶端也在年初的時候也支持了沉浸式狀態(tài)欄。在實現(xiàn)沉浸式狀態(tài)欄效果的過程中踩了不少的坑,特此記錄下來。 下圖為隨手記Android客戶端設置沉浸式狀態(tài)欄前后的效果對比圖:
對比兩種效果,很明顯下面設置了沉浸式狀態(tài)欄的看上去更協(xié)調(diào)、更美觀一點。
2-如何實現(xiàn)沉浸式狀態(tài)欄
2.1-Android 4.4以上實現(xiàn)方式
由于沉浸式狀態(tài)欄設置是在Android 4.4之后才提供的,所以我們需要對Android 4.4以上的系統(tǒng)做適配。Android 4.4有兩種方式可以實現(xiàn)沉浸式狀態(tài)欄,一種是在資源文件中設置,一種是在代碼中設置。
2.1.1-資源文件中設置沉浸式狀態(tài)欄
首先,我們要修改values/styles.xml,在里面添加一個空的style,繼承自BaseTheme。
然后在values-v19目錄下的styles.xml文件(如果項目中沒有就新建一個,在4.4以上的系統(tǒng)就會讀取該目錄下的資源文件)添加如下代碼:
然后將App的主題設置為AppTheme即可。 注:android:windowTranslucentStatus這個屬性是v19開始引入的。
2.1.2-在代碼中設置
在代碼中實現(xiàn)更為方便一點,我們只需要在BaseActivity中添加一個FLAG TRANSLUCENT STATUS的flag即可。
通過上述兩種方法設置之后,效果圖如下:
我們會發(fā)現(xiàn),僅僅通過上述設置Toolbar會頂?shù)綘顟B(tài)欄里面去。通常大家會想到使用fitsSystemWindows屬性來解決此問題。
fitSystemWindows官方描述:Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity. 簡單描述:這個屬性的作用是讓view可以根據(jù)系統(tǒng)窗口(如status bar)來調(diào)整自己的布局,如果值為true,就會調(diào)整view的paingding屬性來給system windows留出空間(即給view添加一個值為狀態(tài)欄高度的top padding)。
我們試著給Toolbar設置一下fitsSystemWindows屬性為true。布局代碼如下:
上面代碼在Android 4.4和Android 5.0+上面對比效果圖如下:
由上面對比圖我們可以看出來,在Android 4.4上面狀態(tài)欄是全透明的,而在Android 5.0+上面狀態(tài)欄是半透明的。
注:有些4.4的系統(tǒng)上面狀態(tài)欄并不是全透明的,而是漸變的。
2.2-Android 5.0以上實現(xiàn)方式
上面已經(jīng)實現(xiàn)了沉浸式狀態(tài)欄的效果了,但是如果運行在Android 5.0以上的機器上面,會發(fā)現(xiàn)大部分手機會出現(xiàn)狀態(tài)欄是半透明的。
也有些App在Android 5.0以上就是這種狀態(tài)欄半透明的效果,比如QQ。但是有些產(chǎn)品和設計就是想統(tǒng)一風格,全部都實現(xiàn)全透明的狀態(tài)欄。那怎么辦呢?Android自5.0起,又為我們提供了設置狀態(tài)欄顏色的API,我們可以自己設置狀態(tài)欄的顏色。
添加上述代碼后再在Android 5.0+上運行看效果,狀態(tài)欄已經(jīng)變成全透明了,和上圖Android 4.4效果一樣的,這里就不再附圖了。
2.3-Android 6.0以上設置狀態(tài)欄字體顏色
大部分手機默認狀態(tài)欄字體顏色是白色的,如果Toolbar或者界面頭部的顏色較淺,那么狀態(tài)欄上白色的字看不怎么清楚。 Android 6.0以后,我們可以使用代碼將狀態(tài)欄字體的顏色設置為黑色了,代碼如下:
3-踩過的坑
本以為上面基本已經(jīng)完美實現(xiàn)了沉浸式狀態(tài)欄了,沒想到測試的時候還是發(fā)現(xiàn)了一系列的坑。
3.1-軟鍵盤彈出時Toolbar被頂上去了
如果在界面中有EditText或者其他輸入框的話,會發(fā)現(xiàn)當軟件盤彈出的時候Toolbar里面的內(nèi)容都被頂上去了,如下圖所示:
這是為什么呢?經(jīng)研究發(fā)現(xiàn)原來是fitsSystemWindows屬性搞的鬼。哪個View設置了fitsSystemWindows=true,這個View就會被軟件盤頂上去。所以說,fitsSystemWindows不能亂用,會有意想不到的坑。 那能不能不用fitsSystemWindows呢?當然可以。前面也說了,fitsSystemWindows=true的作用是給View增加值為狀態(tài)欄高度的padding,那我們何不自己手動給Toolbar添加padding呢? 我們?nèi)サ鬞oolbar上的fitsSystemWindows屬性,并設置一下Toolbar的padding,代碼如下:
去掉Toolbar的fitsSystemWindows屬性,并加上上面的代碼,軟鍵盤彈出時Toolbar正常了。 目前隨手記Android項目中就是使用代碼添加padding的方式替代fitsSystemWindows屬性的。
3.2-軟鍵盤彈出時EditText等輸入框會被軟件盤覆蓋掉
上面軟件盤將Toolbar頂上去的示例圖中,我們還會發(fā)現(xiàn)一個問題,就是軟鍵盤彈出時EditText并沒有跟著彈出來而是被軟鍵盤覆蓋掉了。
上面說Toolbar加了fitsSystemWindows屬性之后會被軟鍵盤頂上去,那么我們給輸入框加一個fitsSystemWindows屬性是否剛好就能解決輸入框被覆蓋的問題呢?果斷試一下!
試了之后發(fā)現(xiàn),果然可以,但是輸入框的高度變了,其實是輸入框的padding增加了狀態(tài)欄的高度。很顯然,這并不是一個很好的解決方式。 后來在stackoverflow上找到了一個解決方法:解決FLAG TRANSLUCENT STATUS導致輸入框被軟鍵盤覆蓋的解決方案
我們對其做了點調(diào)整,代碼如下:
添加上面的類,然后在Activity的onCreate方法中的setContentView后面加上如下代碼:
然后運行,輸入框能夠正常被頂上去,而且輸入框的布局又沒有受到影響。
該方案的原理是,給界面的根布局設置一個監(jiān)聽器,當界面大小有變化的時候,如鍵盤彈出的時候,重新設置一下根布局的高度,再調(diào)用requestLayout對界面進行重繪。
目前隨手記Android就是使用這個方案,截止到目前也沒有發(fā)現(xiàn)這種方案會帶來其他什么問題。
3.3-華為EMUI3.1上的坑
將上面的沉浸式代碼放在EMUI3.1系統(tǒng)的手機(如華為榮耀7)上面跑,會發(fā)現(xiàn)根本沒有沉浸式效果,狀態(tài)欄是透明的,顯示的是桌面上的顏色,如下圖:
經(jīng)驗證,原來是EMUI3.1系統(tǒng)的原因,很多App也是在EMUI3.0上有沉浸式的效果,到了EMUI3.1卻沒有效果了。在EMUI3.1沒有沉浸式效果如果和4.4以前一樣是黑的也就算了,這樣透明的顯示桌面顏色實在難看。 后來發(fā)現(xiàn)去掉下面這句代碼,可以讓其有沉浸式的效果。
效果如下:
不過它的狀態(tài)欄不是全透明的,而是像某些4.4的系統(tǒng)一樣是漸變的,不過總比顯示桌面顏色的效果好。 這里我們加一個判斷,判斷如果不是EMUI3.1的系統(tǒng),才調(diào)用clearFlags清除掉FLAG TRANSLUCENT STATUS。 具體代碼如下:
3.4-CoordinatorLayout+AppBarLayout滾動隱藏導航欄遇到沉浸式狀態(tài)欄的坑
這個坑主要是在做理財頭條需求的時候碰到的。
需求背景:頭條功能需要實現(xiàn)二級TabLayout導航,第一級是Toolbar(頭條、產(chǎn)品和發(fā)現(xiàn)),第二級是是頭條里面各個欄目切換的TabLayout。需要實現(xiàn)的效果是,在頭條Fragment中,滑動帖子列表可以隱藏和顯示一級導航Toolbar。一級導航Toolbar顯示的時候,左右滑動是切換一級導航的Tab(即頭條、發(fā)現(xiàn)和產(chǎn)品)。當在頭條Fragment中上滑滾動帖子列表隱藏一級導航Toolbar后,左右滑動是切換二級導航的tab(即頭條各個欄目)。效果見下圖。
滾動列表隱藏和顯示Toolbar,首先肯定是想到CoordinatorLayout+AppBarLayout。基于項目中已實現(xiàn)的沉浸式效果,添加修改Activity中的布局:
布局是在Toolbar中添加一個TabLayout作為一級導航的tab。然后使用一個ViewPager,給該ViewPager添加了三個Fragment,分別是頭條、產(chǎn)品和發(fā)現(xiàn)的Fragment。其中,頭條Fragment中又嵌套了TabLayout和ViewPager。 基于沉浸式的實現(xiàn)方案,在代碼中給AppBarLayout添加一個狀態(tài)欄高度的padding。本以為可以大功告成了,結(jié)果發(fā)現(xiàn)運行之后,在上滑隱藏AppBarLayout之后再下拉,會超出下拉范圍,也就是下拉的時候會多出一條狀態(tài)欄高度的空白,效果如下圖頂部:
經(jīng)過不斷嘗試和探索,發(fā)現(xiàn)給Activity添加如下flag即可:
嗯,不錯,滑動問題解決了。但心里總是不安,總感覺有坑。后面發(fā)現(xiàn)確實有坑,添加了這個flag后,部分帶虛擬按鍵的華為手機出現(xiàn)虛擬按鍵擋住底部布局的問題,經(jīng)驗證只有EMUI3.1才有這個問題(又是EMUI3.1,已無力吐槽)。 最后百般周折,終于找到有效解決CoordinatorLayout+AppBarLayout并給AppBarLayout設置paddingtop之后的滑動問題的方法了。
本以為上面解決方案已經(jīng)完美沒有任何問題了,沒想到還是有坑。不久后測試發(fā)現(xiàn)一個現(xiàn)網(wǎng)問題:當WebView中的輸入框獲取焦點軟鍵盤彈出后,退出界面時底部布局出現(xiàn)軟鍵盤大小的黑塊。如下圖所示:
經(jīng)排查,此問題就是由于上面那段代碼引起的。 沒辦法,只能去掉上面那段代碼,尋找另外的解決方案來處理CoordinatorLayout+AppBarLayout并給AppBarLayout設置paddingtop的滑動問題了。 后來在發(fā)現(xiàn)在Activity的onCreate方法中加上下面一段代碼就可以完美解決這個問題。
4-總結(jié)
上面就是隨手記Android項目中沉浸式狀態(tài)欄實現(xiàn)過程中遇到的坑以及解決方案。最終隨手記Android實現(xiàn)狀態(tài)欄效果后在不同機型上面效果圖如下:
經(jīng)過沉浸式狀態(tài)欄的開發(fā),發(fā)現(xiàn)幾個容易踩的坑需要注意: 1.fitsSystemWindows=true要慎用,很多坑。比如WebView中輸入框獲取焦點彈出軟鍵盤時出現(xiàn)抖動,還有哪個View設置了fitsSystemWindows=true軟鍵盤彈出時哪個View就會被頂上去; 2.WindowManager.LayoutParams.FLAG LAYOUT NO_LIMITS不要用,會導致EMUI3.1的系統(tǒng)下面虛擬按鍵擋住布局。