透明系統欄及沉浸模式的總結

關於所謂的“沉浸式”,我有許多話要說,因爲這個東西實在是折磨了我許多的時間。實現的方式有許多,兼容性問題也不少。官方文檔也讓我感到也有些雲裏霧裏。那些“長得很相似”的Flag,適用情況很接近的設置方法,讓我不得不一個個測試,然而卻一次次推翻。模擬器上測試;真機上測試;4.4版本上的測試;5.0後版本的測試;有導航欄手機上的測試;老掉牙手機上的測試。總而言之,這個東西的探索讓我深刻地體會到android系統兼容性問題的麻煩。

當你功能開發佔據10%,而處理兼容性問題佔據90%的時候。你不得不思考這樣一個問題:“兼容性真的有那麼重要嗎?”撇開這個問題,以投入和產出的角度顯然是不太划得來的,但是收穫的角度:我學習瞭解了View的加載機制、DecorView在不同版本上的實現以及發展、養成了遇到問題第一時間查閱官方文檔的習慣。

 

關鍵詞

1、系統欄Systembar(包括狀態欄Statusbar,導航欄Navigationbar)

2、內容主體暫且可以理解爲Window中除了Systembar以外的窗口

 

實現原理:

1、將SystemBar透明化

2、根據不同情況決定內容主體是否需要延伸到SystemBar下方

 

一、將SystemBar透明化

主題中設置屬性:

<itemname="android:windowTranslucentStatus">true</item>

<itemname="android:windowTranslucentNavigation">true</item>


代碼中設置Flag:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//透明狀態欄

getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);//透明導航欄


5.0後設置:

     getWindow().setStatusBarColor(Color.TRANSPARENT);

     getWindow().setNavigationBarColor(Color.TRANSPARENT);

 

二、根據不同情況決定內容主體是否需要延伸到SystemBar下

主題中設置屬性:

      <itemname="android:windowFullscreen">true</item>

代碼中設置Flag:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

FLAG_TRANSLUCENT_STATUS單獨使狀態欄透明化

FLAG_TRANSLUCENT_NAVIGATION單獨使導航欄透明化

4.1後更多選項的設置:

ViewdecorView = getWindow().getDecorView();

decorView.setSystemUiVisibility(Flag);

Flag的值需要了解以下幾種情況:

1、SYSTEM_UI_FLAG_FULLSCREEN

2、SYSTEM_UI_FLAG_HIDE_NAVIGATION

3、SYSTEM_UI_FLAG_IMMERSIVE

4、SYSTEM_UI_FLAG_IMMERSIVE_STICKY

5、SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

6、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

7、SYSTEM_UI_FLAG_LAYOUT_STABLE

8、SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

 

SYSTEM_UI_FLAG_FULLSCREEN

隱藏狀態欄,主體向上偏移,但在手動下拉會重新顯示狀態欄並清除該flag。

View.SYSTEM_UI_FLAG_FULLSCREEN |View.SYSTEM_UI_FLAG_LAYOUT_STABLE

限制主體位置保持不變,狀態欄留白

 

SYSTEM_UI_FLAG_HIDE_NAVIGATION

隱藏導航欄,主體向下偏移,由於導航欄太重要了,所以只要和Activity進行任何交互都會重新顯示導航欄,並清除該flag和SYSTEM_UI_FLAG_FULLSCREEN。

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_STABLE

限制主體位置保持不變,導航欄留白

 

SYSTEM_UI_FLAG_LAYOUT_STABLE

保持內容主體位置不變,不隨着Systembar的隱藏顯示而偏移

 

SYSTEM_UI_FLAG_IMMERSIVE

沉浸模式:只能和SYSTEM_UI_FLAG_HIDE_NAVIGATION結合使用。簡單交互不會重新顯示導航欄

 

SYSTEM_UI_FLAG_IMMERSIVE_STICKY

沉浸模式:只能和SYSTEM_UI_FLAG_FULLSCREEN或SYSTEM_UI_FLAG_HIDE_NAVIGATION結合使用。

下拉狀態欄位置可以短暫顯示狀態欄和導航欄,然後再次隱藏。

 

SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

主體佔據全部屏幕,但不隱藏狀態欄,這裏的layout可以理解爲內容主體

 

SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

主體佔據全部屏幕,但不隱藏導航欄,這裏的layout可以理解爲內容主體

 

SYSTEM_UI_FLAG_LAYOUT_STABLE

這個Flag確實有點難以理解,唯一讀懂的是在各種flag切換的時候能保持內容主體的穩定性。如果你不想flag變換的時候內容主體上下偏移,就需要設置SYSTEM_UI_FLAG_LAYOUT_STABLE。

 

View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

淺色狀態欄模式:對應於圖標變深色,防止淺色背景導致狀態欄看不清,不設置是深色模式

 

實踐驗證過程:

View decorView =getWindow().getDecorView();

decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

狀態欄不會隱藏,仍佔據着位置,但內容可以侵入狀態欄下方

 

decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);

View.SYSTEM_UI_FLAG_FULLSCREEN 狀態欄下滑,主體佈局就會下降一個狀態欄的高度,很突兀

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN狀態欄下滑,主體佈局位置不變化

 

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;

這個和單獨使用View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN區別在哪兒,還沒發現

 

andriod系統提供了一些透明狀態欄/導航欄的主題以供使用:

<stylename="Theme.Material.Light.NoActionBar.TranslucentDecor">

    <item name="windowTranslucentStatus">true</item>

    <itemname="windowTranslucentNavigation">true</item>

    <itemname="windowContentOverlay">@null</item>

</style>

 

SYSTEM_UI_FLAG_HIDE_NAVIGATION |SYSTEM_UI_FLAG_IMMERSIVE

SYSTEM_UI_FLAG_IMMERSIVE必須與SYSTEM_UI_FLAG_HIDE_NAVIGATION 結合使用纔有意義

加上SYSTEM_UI_FLAG_IMMERSIVE,則導航欄在隱藏後不會因與界面的交互而呼出。

類似於看視頻時進入全屏模式。

 

SYSTEM_UI_FLAG_FULLSCREEN |SYSTEM_UI_FLAG_HIDE_NAVIGATION

進入全屏模式,但用戶與界面進行最簡單的交互都會重新顯示狀態欄,導航欄

 

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE

進入全屏模式,用戶交互會重新顯示狀態欄,導航欄,不會再隱藏

 

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY

進入全屏模式,用戶交互會短暫顯示狀態欄,導航欄,然後再隱藏

 

總結:

比較簡潔的透明狀態欄的做法是:

方法一:適合4.1及以上

if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){

    View window =getWindow().getDecorView();

    window.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

           |View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    getWindow().setStatusBarColor(Color.TRANSPARENT);

}

對於不需要延伸至狀態欄下方的組件,使用android:fitsSystemWindows="true"將進行設置即可。本質上是添加一個狀態欄高度的paddingTop屬性,所以android:layout_height屬性不要使用"250dp"這種固定數值,可以使用"wrap_content"結合android:minHeight="250dp"的方式。

 

方法二:適合4.1及以上

同方法一:

<itemname="android:windowTranslucentStatus">true</item>

      <itemname="android:windowTranslucentNavigation">true</item>

結合android:fitsSystemWindows屬性

 

方法三:適合4.0及以下的版本

<itemname="android:windowFullscreen">true</item>

      <itemname="android:windowContentOverlay">@null</item>

或者代碼中:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

方法三使用android:fitsSystemWindows是無效的。所以我的做法是給需要向下偏移的組件設置一個paddingTop的值,並在v21,v19等不同版本設置不同的值做兼容(源碼可知statusbar高度是25dp)。這裏默認所有手機平臺都是25dp。

網上的做法是選擇性填充一個View,該View的Height是動態獲取的statusbar的高度。這個做法兼容了不同廠商自己定製的UI使用不同於Google官方25dp的狀態欄高度的情況。如紅米note2是20dp,魅族note5是22dp。

 

另外對於setFlags方法,同樣有單獨設置狀態欄和導航欄的方法:

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    對於這兩個屬性而言,android:fitsSystemWindows有效,但在設置了FLAG_TRANSLUCENT_NAVIGATION的時候,內容主體也會向下偏移一個導航欄的高度,可見這裏的android:fitsSystemWindows指的是SystemBar,而不僅僅是StatusBar。


方法四:適合於5.0以後

結合Design Support庫中的CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout,並且結合使用fitsSystemWindows。這也是我第一個接觸的方法。需要注意的是fitsSystemWindows屬性在CoordinatorLayout中有不同的實現,fitsSystemWindows=“true”反而表示是否出現在狀態欄下方。知道真相的我眼淚掉下來。

方法在Fragment中會有問題(例如在FragmentTabhost中使用),使用replace切換則正常,使用add切換的Fragment則fitSystemWindow失效,必須調用onCreate才能生效,我滴個天~~~~最後我只能放棄fitSystemWindow,使用方法三中的設置paddingTop的方法。


方法五:使用第三方框架

SystemBarTint      https://github.com/jgilfelt/SystemBarTint

StatusBarUtil        https://github.com/laobie/StatusBarUtil

 

備註:

使用Theme的方式相比代碼中設置的優勢:

1、更易於維護,並且不易出錯

2、更順暢的UI過渡,因爲系統在初始化主Activity之前就已經知道了渲染UI所需要的相關信息

 

參考資料:

Managing the System UI

https://developer.android.com/training/system-ui/index.html

SystemBarTint

https://github.com/jgilfelt/SystemBarTint

StatusBarUtil

https://github.com/laobie/StatusBarUtil


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章