與StatusBar和NavigationBar相關的東西有兩種,一是控制它們的顯示與隱藏,二是控制它們的透明與否及背景。 git
在2.3及之前,StatusBar只能顯示與隱藏,即全屏模式,經過WindowManager.LayoutParams.FLAG_FULLSCREEN來實現: github
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
並可經過如下Flag使Activity的佈局可使用整個屏幕,狀態欄會顯示到Activity上方並遮蓋部分佈局 函數
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
在3.0(API 11)中,添加了一個重要的方法:setSystemUiVisibility(int),用於控制包括Status Bar在內的一些窗口裝飾元素的顯示,並添加了View.STATUS_BAR_VISIBLE和View.STATUS_BAR_HIDDEN兩個Flag用於控制Status Bar的顯示與隱藏,但在4.0(API 14)中廢棄了。 佈局
在4.0(API 14)中,Andorid引入了Navigation Bar,並添加了一個Flag:SYSTEM_UI_FLAG_HIDDEN_NAVIGATION用於控制Navigatoin Bar的顯示。3.0中被棄用的View.STATUS_BAR_VISIBLE被View.SYSTEM_UI_VISIBLE替代,View.STATUS_BAR_HIDDEN被View.SYSTEM_UI_LOW_PROFILE替代,View.SYSTEM_UI_LOW_PROFILE不會使Status Bar和Navigation Bar消失,而是會使它們變暗,下降它們對視覺的干擾,使用戶能夠專一於應用的內容,但仍可響應用戶的交互,當和它們的交互發生時,會退出Low Profile的狀態。 spa
在4.1(API 16)中,對Status Bar和Navigation Bar的控制進一步加強,引入了View.SYSTEM_UI_FLAG_FULLSCREEN,和View.SYSTEM_UI_HIDDEN_NAVIGATION分別控制Status Bar和Navigation Bar的顯示。並同時引入了另外三個Flag:View.SYSTEM_UI_FLAG_LAYOUT_STABLE、View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION。 3d
顯示System UI: code
getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
隱藏System UI 視頻
getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
經過上面那段View.SYSTEM_UI_FLAG_FULLSCREEN、View.SYSTEM_UI_FLAG_HIDE_NAVIGATION相關的代碼實現的全屏模式,能夠隱藏掉Status Bar和Navigation Bar,可是這些都是很重要的功能,尤爲是Navigation Bar,對於只有虛擬按鍵的手機,若是隱藏掉Navigation Bar,連切換程序都作不到,因此,當用戶和手機有任何交互的時候都會從新顯示Status Bar和Navigation Bar,這被稱爲LEAN BACK模式。這很適合視頻播放的場景,但對於其餘一些場景可能就不適合了,好比讀書。 繼承
因此,在4.4(API 19)中引入了沉浸模式View.SYSTEM_UI_FLAG_IMMERSIVE和View.SYSTEM_UI_FLAG_IMMERSIVE_STICK。在IMMERSIVE模式中,用戶的普通交互並不會使系統退出IMMERSIVE模式,若是要退出IMMERSIVE模式,須要在屏幕的頂部或底部向內滑動。這可使用戶專一於內容,但退出方式並不像LEAN BACK模式那麼明顯,因此在第一次進入IMMERSIVE時,系統會彈出一個UI提醒退出的方法。SYSTEM_UI_FLAG_IMMERSIVE等須要和SYSTEM_UI_FLAG_FULLSCREEN、SYSTEM_UI_FLAG_HIDE_NAVIGATION一塊兒使用。 get
IMMERSIVE_STICKY和IMMERSIVE的區別是,在IMMERSIVE中,用戶從屏幕頂部或底部向內滑動時會退出IMMERSIVE模式,須要手動控制再次進入IMMERSIVE模式,而在IMMERSIVE_STICKY模式中,一樣的操做只會使系統以半透明方法顯示System UI方便用戶操做,並會在一段時間後自動隱藏,此時並不會引發onSystemUiVibilityChanged的調用。
能夠看到,關於全屏,關於System UI的控制,若是想有好的體驗,仍是有不少細節須要處理的,不過幸虧,chrisbanes大神寫了一個類來處理這些細節:https://gist.github.com/chrisbanes/73de18faffca571f7292
除了控制System UI的顯示和隱藏外,還可使它們變成透明的,在4.4(API 19)中還引入了WindowManager.LayoutParams.FLAG_TRANSUCENT_STATUS和WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION用於控制System UI變透明,這兩個Flag分別對應於windowTranslucentStatus和windowTranslucentNavigation兩個attr,並同時提供了相應的Theme(這些Theme都沒有ActionBar),當使用這兩個Flag時,SYSTEM_UI_FLAG_LAYOUT_STABLE、SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION會被自動添加。
當System UI變透明後,Activity的UI佔據整個屏幕,System UI覆蓋在Activity的UI上面,對於通常的應用,雖然System UI透明瞭,但會發現效果並無那麼好,由於ActionBar仍是在Status Bar下面,Status Bar變爲透明後會透出Activity的UI,通常狀況下這部分UI和ActionBar放到一塊兒並非那麼協調。能夠設置窗口的背景和ActionBar的色調一致,但會引發OverDraw,而且若是指定的佈局撐不滿全屏呢?
咱們知道,Activity頂部的佈局是DecorView,而DecorView繼承自FrameLayout,因此能夠添加兩個View到DecorView中,佔據Status Bar和Navigation Bar的位置,並設置它們的背景使其與ActionBar相配,但這須要計算Status Bar和Navigation Bar的大小,而且須要判斷Navigation Bar的位置(Bottom or Right)。正好,也有人作了這樣的事:https://github.com/jgilfelt/SystemBarTint
不管是LEAN_BACK模式仍是IMMERSIVE模式,都使用到了4.1中引入的三個Flag:SYSTEM_UI_FLAG_LAYOUT_STABLE、SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,它們的用處並不像SYSTEM_UI_FLAG_FULLSCREEN那麼明顯,並且名稱還很類似,要了解它們的用處,須要先了解一個內容邊襯區的概念(ContentInset)及一個重要的函數View.fitSystemWindows。
默認狀況下,應用程序窗口在Status Bar下面,系統已經處理好了應用窗口的顯示,咱們不須要關心Inset和fitSystemWindow,但有一些狀況就要咱們本身處理了。
當使用了Translucent System UI或SYSTEM_UI_FLAG_FULLSCREEN等時,Activity的UI能夠顯示到System UI下面,System UI在顯示時可能會蓋住Activity的UI,因此可能須要處理這樣的狀況,這就須要知道System UI會佔用的空間大小是多少,這個大小就是內容邊初區(ContentInset),系統會經過fitSystemWindows(Rect)來通知咱們,咱們能夠經過這個方法調整咱們的內容顯示。
還有一個方法是View.setFitSystemWindows(boolean),用於設置是否使用系統默認的fitSystemWindows實現。系統的默認實現會消耗掉內容邊襯區空間的佔用,算到View的Padding裏面,並返回true,不然什麼也不作返回false,當返回false時,會繼續調用View Hierarchy中其餘View的fitSystemWindows,直到某一個View中返回true,調用順序是深度優先。若是咱們決定本身處理System UI的空間佔用,能夠重寫VIew的fitSystemWIndows並返回true,若是本身只是作些處理,仍想調用系統的默認實現,要記得調用super.fitSystemWindows並返回false。
接下來就能夠說SYSTEM_UI_FLAG_LAYOUT_STABLE等的做用了。在使用View.SYSTEM_UI_FLAG_FULLSCREEN|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION時,Status Bar和Navigation Bar都會隱藏,Activity的UI佔據整個屏幕,當System UI再次顯示時,應用程序窗口會被Resize,爲System UI騰出空間,這會引發屏幕的跳動,這三個Flag的做用就在於此,SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION要和SYSTEM_UI_FLAG_LAYOUT_STABLE一塊兒使用,用於控制當Status Bar或Navigation Bar顯示或隱藏時,Activity的UI是否會被Resize,當使用這三個Flag時,Activity會佔用整個屏幕空間,並經過fitSystemWindows傳入的Insets標明Insets的大小(對於SYSTEM_UI_FLAG_FULLSCREEN會同時包含ActionBar的大小),咱們能夠根據這個Insets的大小調整內容的顯示,若是給ContentView設置fitSystemWindows爲true,會自動把Iinset轉化爲padding。
有幾點須要注意的是:
參考:https://www.youtube.com/watch?v=cBi8fjv90E4&feature=youtu.be