1. 程式人生 > >Android 系統狀態列沉浸式/透明化完整解決方案

Android 系統狀態列沉浸式/透明化完整解決方案

前言

網上已經有很多有關於系統狀態列的解決方案,這篇文章也不會有什麼新奇的解決方案,都是本人經過自己試驗,統計提煉出來的相對靠譜的一套解決方案.
 

關於術語

網上有很多爭論:

你這狀態列是變色龍狀態列,不是沉浸式的
這應該是沉浸式的狀態列吧,系統欄與actionbar顏色設為一致

我只想說去你妹的,老子只要自己的app的狀態列能和主題顏色一致就行了,定義那麼多術語,讓我等小白情以何堪?
吐槽歸吐槽,但還是不得不去試著理解下這些術語怎麼來的,引用這裡的一段話:

  1. 沉浸式全屏模式
    隱藏status bar(狀態列)使螢幕全屏,讓Activity接收所有的(整個螢幕的)觸控事件。
  2. 透明化系統狀態列
    透明化系統狀態列,使得佈局侵入系統欄的後面,必須啟用fitsSystemWindows屬性來調整佈局才不至於被系統欄覆蓋。

因此,我就這樣理解了:

沉浸式不就是隱藏狀態列嘛,狀態列不見了?這不就是app全屏模式嘛?wtf?

而透明式式狀態列就是讓app的內容佈局可以擴充套件到系統狀態列?這裡有個問題就是為什麼能在系統狀態列還顯示的情況下,將內容佈局擴充套件到系統狀態列?恩,這應該很好理解,就是Z座標系的作用了,系統狀態列是覆蓋在內容佈局上面的,並且是透明的。

貌似這裡所謂透明化系統狀態列才是本菜想要的,不管了,現在開始一一試驗,至於這概念理解的對不對,管他呢?那到底應該叫什麼,那我就叫自適應狀態列

,行不行?

前提條件

讓系統狀態列顏色隨app主題顏色變化而變化這一設計,毫無疑問,也是向ios學習的:從android4.4開始引進的,並且在5.0進行了改進。因此,也只能將這一特性應用在android4.4以上的手機,無法做到全部適配。記得stormzhang(貌似是)曾說過:

作為一個android程式設計師,還能有什麼比做出ios風格的app更感到悲哀的呢?哎...

兩種情況下的解決方案:

  1. 使用toolbar
    這種方案相對簡單,個人喜歡這種方案,本菜雖菜,但喜歡緊跟潮流。toolbar太好用了,
  2. 不使用toolbar

1. 使用toolbar的解決方案

這個方法參照了這裡,薄荷app的toolbar適配方案

其基本原理就是:
theme裡新增style: <item name="android:windowTranslucentStatus"> true </item>後,包含toolbar的內容佈局就可以擴充套件至系統狀態列,狀態列會覆蓋在toolbar上,如果此時使用android:fitsSystemWindows="true",就可以調整內容佈局(估計也是在根佈局上加padding)恢復到原來位置.但是,上面的解決方案確是給toolbar加上一個padding-top="25dp",這樣就可以做到系統狀態列的顏色和toolbar的顏色保持一致.具體方案可以參照上面的薄荷app的方案連結.


簡述下步驟(只是簡述,有疑問請參照上面薄荷app的連結即可):

  1. 引入v7包,並在佈局裡新增toolbar
    compile ‘com.android.support:appcompat-v7:22.2.1’
  2. 在程式碼中設定透明化:
     
    1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

    2. WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();

    3. local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);

    4. }

    當然也可以在theme的樣式檔案裡新增style:<item name="android:windowTranslucentStatus">true</item>,效果相同,但是大神們都說樣式檔案裡設定在某些型號裡不生效.ok,大家都在程式碼裡設定就好了
  3. 給toolbar加上padding-top,toolbar程式碼如下
     
    1. <android.support.v7.widget.Toolbar android:id="@+id/toolbar"

    2. android:layout_width="match_parent"

    3. android:layout_height="wrap_content"

    4. android:paddingTop="@dimen/toolbar_padding_top"

    5. app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

    6. app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

    7. android:background="#30469b">

    8. <TextView android:layout_gravity="center"

    9. android:layout_width="wrap_content"

    10. android:layout_height="wrap_content"

    11. android:textSize="20sp"

    12. android:text="@string/app_name"/>

    13. </android.support.v7.widget.Toolbar>

    4.其中android:paddingTop="@dimen/toolbar_padding_top"要在values中的styles檔案裡設為0dp,在values-v19的styles裡設為25dp,原因不多說了

這樣就可以達成了我們的目標,如果只是這樣也就罷了,按照上面做就可以了,關鍵是本菜是喜歡緊跟潮流的,使用MD風格的DrawerLayout+NavigationView時,在android4.4的手機下,就會變這樣了:

android4.4上的效果


很明顯,drawerlayout並沒用被擴充套件至系統狀態列,但在android5.0以上效果還是可以的,這讓我很奇怪,只能歸咎於5.0的優化了

android5.0上的效果


經過各種折騰終於想起來,可以把fitsSystemWindows的特性用在drawerlayout上試試,最後發現居然可以,最終將設定windowTranslucentStatus的程式碼調整如下:

 
  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

  2. WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();

  3. local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);

  4. if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){

  5. //將側邊欄頂部延伸至status bar

  6. mDrawerLayout.setFitsSystemWindows(true);

  7. //將主頁面頂部延伸至status bar;雖預設為false,但經測試,DrawerLayout需顯示設定

  8. mDrawerLayout.setClipToPadding(false);

  9. }

  10. }

最終android4.4上也可以顯示正常:

android4.4上修正後的效果

2. 不使用toolbar的解決方案

不使用toolbar時,而是actionbar時,因為actionbar不好定製,所以無法採用上面那個方法,只能採用其它方法,這裡的方案主要參考這裡:Translucent System Bar 的最佳實踐
這篇簡書看的本菜暈乎乎的,仔細看下來,其實都是基於一個原理:
不管有沒有actionbar,內容佈局的背景顏色一律設為主題顏色,然後有actionbar的話,就將actionbar與內容佈局的背景顏色同時設為主題顏色,然後,每個內容佈局的根佈局都要設上fitsSystemWindows="true"進行調整,感覺超麻煩有沒有?


不說多少,簡述步驟:

  1. 在程式碼中設定透明化,步驟同上
  2. 設定內容佈局的根佈局的背景顏色為主題顏色,同時設定fitsSystemWindows="true"
     
    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    2. android:layout_width="match_parent"

    3. android:layout_height="match_parent"

    4. android:background="@color/colorPrimary"

    5. android:fitsSystemWindows="true"

    6. android:orientation="vertical">

  3. 在內容佈局的下面再設定一層內容佈局,設背景顏色為白色(或其它顏色):
     
    1. <LinearLayout android:layout_width="match_parent"

    2. android:layout_height="match_parent"

    3. android:background="@color/c_light_white"

    4. android:gravity="center"

    5. android:orientation="vertical">

至此,這種方案也完成了,看下效果:

android4.4上的效果

可以看出,這種方案一般情況下,還是可行的,但是有三個問題:

  1. 如果用上drawerlayout+navigationview,actionbar就會覆蓋在側邊欄上(如上圖),暫時未找到解決方案,但是我想說你都用drawerlayout+navigationview了,為何不用toolbar,因此這個問題應該不是問題,況且還可以使用其它的側邊欄實現方式,各位道友可以試試
  2. 這種方案在每個根佈局上都要設fitsSystemWindows="true"進行調整,當然上面也有優化方案,可仍然覺得很麻煩,
  3. 每個根佈局裡都要多加的一層佈局來覆蓋根佈局的背景主題顏色

因此,這種方案的確不是上上之選.

總結

本文主要在考慮使用標題欄(actionbar/toolbar)的情況下,做出的方案,當然你也可以自定義標題欄,或者不使用標題欄;其實都可以基於上面一樣的道理:

在狀態列透明化的前提下,調整頂部view的padding-top,來達到狀態列自適應一體化的目的

網上還有其它蠻多的解決方案,如:

  1. 使用開源庫SystemBarTint,這個庫也挺不錯的,可以動態改變系統狀態列顏色,但是作者已經2年沒有維護了,現在技術更新迭代這麼快,鬼知道這個庫會不會出現什麼問題,因此可以放棄使用了
  2. 如果不怕麻煩,還可以new一個高度和狀態列一樣高的view,插入到內容佈局的上面,但是,想想都覺得麻煩,我也懶得試了

---------------------------------------------------------------

 

getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
這個屬性4.4算是全透明(有的機子是過渡形式的透明),5.0就是半透明瞭 我的模擬器、真機都是半透明,
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){//4.4 全透明狀態列
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0 全透明實現
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);//calculateStatusColor(Color.WHITE, (int) alphaValue)
}
以上程式碼基本解決適配各種版本全透明狀態列(如導航欄有需求可以再加導航欄)