管理System windows(status bar和navigation bar)

概述

通常來說,System bars(包含status bar和navigation bar,如下圖所示, 1代表status bar,2代表navigation bar)會和你的應用同時顯示在屏幕上。應用為了可以沉浸式的顯示內容,例如播放電影和圖片的效果,可以通過暫時淡化System bars圖標來實現減少分散用戶注意力的體驗,或者通過暫時隱藏System bars來實現完全沉浸式的的體驗。

下面介紹在不同版本Android系統中如何通過淡化或者隱藏System bars的方式來營造沉浸式的用戶體驗,同時仍保留輕松訪問System bars。

1 淡化System bars(Dimming the System Bars)
本部分介紹怎么在Android4.0(API級別14)和更高的版本中淡化System bars。 在早期版本的Android系統中Android沒有提供內置的淡化System bars的方法。
淡化System bars的方式并不會改變UI的大小,只是status bar和navigation bar的相關圖標會被弱化,比如navigation bar的幾個虛擬鍵會弱化成很細微的小點。一旦你再次觸摸 status bar和navigation bar 的所在區域,他們就會再次顯示。這種方式的好處是status bar和navigation bar仍然顯示在屏幕上,但是它們的細節變模糊了,從而在不犧牲輕松訪問System bars的前提下創建了一個沉浸式的用戶體驗。

代碼實現如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    dimSystemBars();   
}

private void dimSystemBars() {
    // This example uses decor view, but you can use any visible view.
    View decorView = getWindow().getDecorView();
    int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
    decorView.setSystemUiVisibility(uiOptions);
}

只要用戶觸摸了status bar和navigation bar 的所在區域,SYSTEM_UI_FLAG_LOW_PROFILE標志就會被清除,使得status bar和navigation bar淡化效果消失。如果你的應用想要再次淡化status bar和navigation bar,你就需要重新如上設置。

下面這張圖展示了在gallery應用中顯示一張圖片時, navigation bar被淡化的效果(注意status bar沒有顯示并不是淡化了,而是gallery應用直接隱藏了它),請注意,navigation bar (圖像的右側)上有淡淡的灰白色的小圓點表示導航控件,這就是navigation bar 被淡化后的效果。

下圖展示了System bars完全顯示的效果:

上面提到,當System bars處于淡化的情況時,一旦你點擊 status bar和navigation bar 的所在區域,SYSTEM_UI_FLAG_LOW_PROFILE標志就會被清除,使得status bar和navigation bar淡化效果消失。你也可以通過如下代碼手動清除SYSTEM_UI_FLAG_LOW_PROFILE標志:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    cancelDimSystemBars();
}

private void cancelDimSystemBars() {
    View decorView = getWindow().getDecorView();
    // Calling setSystemUiVisibility() with a value of 0 clears
    // all flags.
    decorView.setSystemUiVisibility(0);
}

2 隱藏System bars(Hiding the System bars)

1> 隱藏Status Bar(Hiding the Status Bar)
本小節介紹在不同的Android版本中如何隱藏狀態欄。隱藏狀態欄讓App的content區域——Activity中setContentView()中傳入的就是content——使用更多的顯示空間,從而提供更加沉浸式的用戶體驗。

下圖展示了Status Bar可見時的app:


下圖展示了Status Bar隱藏時的app。請注意,Action Bar也被隱藏了,我們認為當Status Bar和Action Bar應該被同時隱藏。


在android4.0(API 14)及以下的版本中,你可以通過設置WindowManager
的flag來隱藏status bar。有兩種方式來設置WindowManager
的flag,一是在java代碼中動態設置,二是在manifest文件中設置activity的theme。如果你的status bar想要一直隱藏的話,在manifest中設置activity的theme是最好的方式,例如:

<application
    ...
    android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
    ...
</application>

在manifest中設置activity theme的方式的優點如下:
<1> 更容易維護,相對于在java代碼中動態設置的方式更不容易出錯。
<2> UI過渡更加的流暢,因為系統在實例化Activity之前就已經獲得了渲染UI所需要的消息。

另外,可以通過在java代碼中動態設置WindowManager的flag。這種方式可以更加靈活地隱藏和顯示狀態欄:

private void hideStatusBar() {
    // If the Android version is lower than Jellybean, use this call to hide
    // the status bar.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }
}

private void cancelHideStatusBar() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }
}

在Android 4.1 (API 16)或者更高版本中,你可以使用setSystemUiVisibility()來隱藏Status Bar,使用setSystemUiVisibility()設置UI flag比設置WindowManager flag的方式擁有更多的控制選項,隱藏Status Bar的示例代碼如下:

private void hideStatusBar() {
    // If the Android version is lower than Jellybean, use this call to hide
    // the status bar.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    } else {
        View decorView = getActivity().getWindow().getDecorView();
        // Hide the status bar.
        int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
    }
}

以下幾點需要注意:
<1> 一旦UI flag被清空(比如跳轉到另外的Activity),如果你想要再一次隱藏Status Bar,那么你需要重寫設置UI flag。為了對Status Bar的隱藏和顯示做出及時的響應,可以通過監聽System bars可見性的改變:

View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
        (new View.OnSystemUiVisibilityChangeListener() {
    @Override
    public void onSystemUiVisibilityChange(int visibility) {
        // Note that system bars will only be "visible" if none of the
        // LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
        if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
            // TODO: The system bars are visible. Make any desired
            // adjustments to your UI, such as showing the action bar or
            // other navigational controls.
        } else {
            // TODO: The system bars are NOT visible. Make any desired
            // adjustments to your UI, such as hiding the action bar or
            // other navigational controls.
        }
    }
});

<2> 在不同的地方設置Ui flag是有區別的,如果你是在onCreate中設置UI flag隱藏System bars,當用戶點擊home鍵,System bars將重新出現,用戶重新回到這個activity的時候,onCreate是不會被調用的,所以System bars仍然是可見的。因此如果你想在activity切換回來的時候仍然保持System bars的狀態,最好是在onResume()或者onWindowFocusChanged()方法中設置UI flag。
<3> 只有當調用setSystemUiVisibility()的view是可見的setSystemUiVisibility()才會起作用。
<4> 界面的切換會導致setSystemUiVisibility()的設置被清空

讓App的content區域——Activity中setContentView()中傳入的就是content——顯示在Status Bar后面
在 android4.1(API 16)以及更高的版本,可以讓App的content區域出現在Status Bar的后面,Status Bar不再影響到實際內容的空間擺放(以前App的content總是在Status Bar下面),因此在Status Bar顯示或者隱藏時,App的content區域的大小就不會發生變化。要達到這種效果只需使用SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN標志(flag)。同時,你也有可能需要SYSTEM_UI_FLAG_LAYOUT_STABLE這個標志來幫助你的應用維持一個穩定的布局(這句話不理解)。

因為App的content區域在Status Bar之后,這個時候你就需要確保app中需要操作的區域不會被Status Bar擋住,不過大多數情況下在布局文件中添加android:fitsSystemWindows屬性(值為true)可以解決Status Bar擋住App的content區域的問題,因為該設置可以調整ViewGroup的padding,為系統控件預留一定的區域。對于絕大多數應用來說,這樣做已經足夠了。

為什么我們要讓App的content區域出現在Status Bar的后面:既然無論如何都需要預留空間給Status Bar ,那為什么我們還需要將App的content區域顯示在Status Bar的后面呢?這一般是為了滿足這種需求:
在一個顯示圖片的列表(如RecycleView)中,當列表滾動的時候,我們希望Status Bar背后是有內容在滾動的,但是當列表滑到了頂端,我又希望列表是沒有被Status Bar擋住的。

在一些情況下,你可能需要修改默認的padding大小來獲取合適的布局。為了控制App的content區域的布局相對System bars(它占據了一個叫做“內容嵌入”content insets的區域)的位置,你可以重寫fitSystemWindows(Rect insets)方法。當窗口的content insets區域發生變化時,fitSystemWindows()方法會被view hierarchy調用,從而讓View做出相應的調整適應。

fitSystemWindows方法中的參數insets的值就是System bars占有空間的大小

2> 隱藏Navigation Bar(Hiding the Navigation Bar)
本小節介紹如何隱藏Navigation Bar,這是在Android 4.0時(API級別14)推出的。

雖然本小節關注隱藏Navigation Bar,但是在實際的開發中,你最好同時隱藏Navigation Bar和Status Bar,隱藏Navigation Bar與Status Bar(在保證易于再次訪問的情況下)使App的content區域占據了整個顯示空間,從而提供了一個更加沉浸式的用戶體驗。

通過SYSTEM_UI_FLAG_HIDE_NAVIGATION flag來隱藏Navigation Bar,同時隱藏Navigation Bar和Status Bar的代碼如下:

private void hideNavigationBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        View decorView = getActivity().getWindow().getDecorView();
        // Hide both the navigation bar and the status bar.
        // SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as
        // a general rule, you should design your app to hide the status bar whenever you
        // hide the navigation bar.
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
    }
}

以下幾點需要注意:
<1> 使用這個方法時,觸摸屏幕的任何地方都會使Navigation Bar和Status Bar重新顯示。用戶的交互使這個標志SYSTEM_UI_FLAG_HIDE_NAVIGATION被清除。

<2> 一旦標志SYSTEM_UI_FLAG_HIDE_NAVIGATION被清空,如果你想要再一次隱藏Navigation Bar和Status Bar,那么你需要重置標志SYSTEM_UI_FLAG_HIDE_NAVIGATION。為了對Navigation Bar和Status Bar的隱藏和顯示做出及時的響應,可以通過監聽System bars可見性的改變,方法與1>中相同。

<3> 在不同的地方設置Ui flag是有區別的,如果你是在onCreate中設置UI flag隱藏System bars,當用戶點擊home鍵,System bars將重新出現,用戶重新回到這個activity的時候,onCreate是不會被調用的,所以System bars仍然是可見的。因此如果你想在activity切換回來的時候仍然保持System bars的狀態,最好是在onResume()或者onWindowFocusChanged()方法中設置UI flag。

<4> 只有當調用setSystemUiVisibility()的view是可見的setSystemUiVisibility()才會起作用。

<5> 界面的切換會導致setSystemUiVisibility()的設置被清空

讓App的content區域——Activity中setContentView()中傳入的就是content——顯示在Navigation Bar后面
在 android4.1(API 16)以及更高的版本,可以讓App的content區域出現在Navigation Bar的后面,Navigation Bar不再影響到實際內容的空間擺放(以前App的content總是在Navigation Bar上面),因此在Navigation Bar顯示或者隱藏時,App的content區域的大小就不會發生變化。要達到這種效果只需使用SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION標志(flag)。同時,你也有可能需要SYSTEM_UI_FLAG_LAYOUT_STABLE這個標志來幫助你的應用維持一個穩定的布局(這句話不理解)。

當你使用這種方法的時候,這個時候你就需要確保app中需要操作的區域不會被System bars遮蓋。更詳細的說明1>中關于這一點的討論。

3 全屏沉浸模式(Using Immersive Full-Screen Mode)
在android4.4及以上版本中為setSystemUiVisibility()方法
引入了一個新的flag:SYSTEM_UI_FLAG_IMMERSIVE,它可以使你的app實現真正意義上的全屏體驗。當SYSTEM_UI_FLAG_IMMERSIVE、SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN三個flag一起使用的時候,可以隱藏Navigation Bar和Status Bar,同時讓你的app可以捕捉到用戶的所有觸摸屏事件。

當沉浸式全屏模式啟用的時候,你的activity會繼續接受各類的觸摸事件。用戶可以通過在Navigation Bar和Status Bar原來區域的邊緣向內滑動讓Navigation Bar和Status Bar重新顯示。這個操作清空了SYSTEM_UI_FLAG_HIDE_NAVIGATION(和SYSTEM_UI_FLAG_FULLSCREEN,如果有的話)兩個標志,因此System bars重新變得可見。如果設置了的話,這個操作同時也觸發了View.OnSystemUiVisibilityChangeListener。然而, 如果你想讓System bars在一段時間后自動隱藏的話,你應該使用SYSTEM_UI_FLAG_IMMERSIVE_STICKY
標簽。請注意,'sticky'版本的flag不會觸發任何的監聽器,因為在這個模式下展示的System bars是處于暫時的狀態。
下圖展示了各種不同的“沉浸式”狀態


非沉浸模式 —— 展示了應用進入沉浸模式之前的狀態。也展示了設置IMMERSIVE標簽后用戶滑動展示系統欄的狀態。用戶滑動后,SYSTEM_UI_FLAG_HIDE_NAVIGATION
和SYSTEM_UI_FLAG_FULLSCREEN就會被清除,系統欄就會重新顯示并保持可見。請注意,最好的方式就是讓所有的UI控件與系統欄的顯示隱藏保持同步,這樣可以減少屏幕顯示所處的狀態,同時提供了更無縫平滑的用戶體驗。因此所有的UI控件跟隨系統欄一同顯示。一旦應用進入了沉浸模式,UI控件也跟隨著系統欄一同隱藏。為了確保UI的可見性與系統欄保持一致,我們需要一個監聽器View.OnSystemUiVisibilityChangeListener來監聽系統欄的變化。這在1>中將已經詳細講解。

提示氣泡——第一次進入沉浸模式時,系統將會顯示一個提示氣泡,提示用戶如何再讓系統欄顯示出來。請注意,如果為了測試你想強制顯示提示氣泡,你可以先將應用設為沉浸模式,然后按下電源鍵進入鎖屏模式,并在5秒中之后打開屏幕。

沉浸模式—— 這張圖展示了隱藏了系統欄和其他UI控件的狀態。你可以設置IMMERSIVE或者IMMERSIVE_STICKY來進入這個狀態。

粘性標簽——這就是你設置了IMMERSIVE_STICKY標簽時的UI狀態,用戶會向內滑動以展示系統欄。半透明的系統欄會臨時的進行顯示,一段時間后自動隱藏。滑動的操作并不會清空任何標簽,也不會觸發系統UI可見性的監聽器,因為暫時顯示的導航欄并不被認為是一種可見的狀態。

注意,immersive類的標簽只有在與SYSTEM_UI_FLAG_HIDE_NAVIGATION,SYSTEM_UI_FLAG_FULLSCREEN中一個或兩個一起使用的時候才會生效。你可以只使用其中的一個,但是一般情況下你需要同時隱藏狀態欄和導航欄以達到沉浸的效果。

如何確定使用哪種沉浸模式
SYSTEM_UI_FLAG_IMMERSIVE與SYSTEM_UI_FLAG_IMMERSIVE_STICKY都提供了沉浸式的體驗,但是在上面的描述中,他們是不一樣的,下面講解一下什么時候該用哪一種標簽。
如果你在寫一款圖書瀏覽器、新聞雜志閱讀器,請將IMMERSIVE標簽與SYSTEM_UI_FLAG_FULLSCREEN,SYSTEM_UI_FLAG_HIDE_NAVIGATION
一起使用。因為用戶可能會經常訪問Action Bar和一些UI控件,又不希望在翻頁的時候有其他的東西進行干擾。IMMERSIVE在該種情況下就是個很好的選擇。

如果你在打造一款真正的沉浸式應用,而且你希望屏幕邊緣的區域也可以與用戶進行交互,并且他們也不會經常訪問系統UI。這個時候就要將IMMERSIVE_STICKY和SYSTEM_UI_FLAG_FULLSCREEN
SYSTEM_UI_FLAG_HIDE_NAVIGATION兩個標簽一起使用。比如做一款游戲或者繪圖應用就很合適。

如果你在打造一款視頻播放器,并且需要少量的用戶交互操作。你可能就需要之前版本的一些方法了(從Android 4.0開始)。對于這種應用,簡單的使用SYSTEM_UI_FLAG_FULLSCREEN與SYSTEM_UI_FLAG_HIDE_NAVIGATION就足夠了,不需要使用immersive標簽。

使用非STICKY的沉浸模式

當你使用SYSTEM_UI_FLAG_IMMERSIVE
標簽的時候,它是基于其他設置過的標簽(SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN)來隱藏System bars的。當用戶向內滑動,系統欄重新顯示并保持可見。用其他的UI標簽(如SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION和SYSTEM_UI_FLAG_LAYOUT_STABLE)來防止System bars隱藏時內容區域大小發生變化是一種很不錯的方法。你也需要確保Action Bar和其他系統UI控件同時進行隱藏。下面這段代碼展示了如何在不改變內容區域大小的情況下,隱藏與顯示狀態欄和導航欄。

// This snippet hides the system bars.
private void hideSystemUI() {
    // Set the IMMERSIVE flag.
    // Set the content to appear under the system bars so that the content
    // doesn't resize when the system bars hide and show.
    mDecorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
            | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
            | View.SYSTEM_UI_FLAG_IMMERSIVE);
}

// This snippet shows the system bars. It does this by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
    mDecorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}

你可能同時也希望在如下的幾種情況下使用IMMERSIVE標簽來提供更好的用戶體驗:

<1> 注冊一個監聽器來監聽System bars的變化。
<2> 實現onWindowFocusChanged()函數。如果窗口獲取了焦點,你可能需要對系統欄進行隱藏。如果窗口失去了焦點,比如說彈出了一個對話框或菜單,你可能需要取消那些將要在Handler.postDelayed()或其他地方的隱藏操作。
<3> 實現一個GestureDetector,它監聽了onSingleTapUp(MotionEvent)事件。可以使用戶點擊內容區域來切換系統欄的顯示狀態。單純的點擊監聽可能不是最好的解決方案,因為當用戶在屏幕上拖動手指的時候(假設點擊的內容占據了整個屏幕),這個事件也會被觸發。

使用STICKY的沉浸模式

當使用了SYSTEM_UI_FLAG_IMMERSIVE_STICKY標簽的時候,向內滑動的操作會讓System bars臨時顯示,并處于半透明的狀態。此時沒有標簽會被清除,系統UI可見性監聽器也不會被觸發。如果用戶沒有進行操作,系統欄會在一段時間內自動隱藏。
圖2展示了當使用IMMERSIVE_STICKY標簽時,半透明的系統欄展示與又隱藏的狀態。


下面是一段實現代碼。一旦窗口獲取了焦點,只要簡單的設置IMMERSIVE_STICKY與上面討論過的其他標簽即可。

@Override
public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}
}

上面關于System bars的淡化、隱藏、沉浸式都是翻譯的google文檔


上面詳細講解了通過System bars的淡化、隱藏、沉浸式來實現沉浸式的效果,下面我們來看看通過System bars的透明化來實現更加沉浸式的效果。

4 透明化System bars
從 3.0 (honeycomb) 開始,Navigation Bar采用虛擬鍵,一直都占據一塊不小的空間,對很多人來說,整個屏幕無法充利用,是一件相當痛苦的事情。也因此,有些人會刻意去挑選仍維持著實體鍵設計的手機。
而 Google 似乎也意識到這個狀況,從 4.4 (KitKat) 提供了開發者一個新的作法,讓最上方的狀態欄 (Status Bar) 以及最下方的導航欄 (Navigation Bar) 可以被透明化,并讓 APP 的內容可以往上下延伸,使整個畫面的可被利用度大幅提升。

實現方式有兩種,一種是Java代碼動態設置,一種是設置Activity的theme,實現代碼如下所示:

1. Java代碼動態設置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //透明狀態欄
    //getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //透明導航欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    //Android 5.x開始需要把顏色設置透明,否則導航欄會呈現系統默認的淺灰色
    getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent));
} else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    //透明狀態欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //透明導航欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}

設置Activity的theme:
2. 在values、values-v19、values-v21的style.xml都設置一個 Translucent System Bar 風格的Theme

values/style.xml:
<style name="ImageTranslucentTheme" parent="AppTheme">
    <!--在Android 4.4之前的版本上運行,直接跟隨系統主題-->
</style>

values-v19/style.xml:
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowTranslucentNavigation">true</item>
</style>

values-v21/style.xml:
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowTranslucentStatus">false</item>
    <item name="android:windowTranslucentNavigation">true</item>
    <!--Android 5.x開始需要把顏色設置透明,否則導航欄會呈現系統默認的淺灰色-->
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

無論使用上面的那一種方式,都會存在一個問題,app的content區域——Activity中setContentView()中傳入的就是content——就會在System bars后面顯示,這時app中需要操作的區域就有可能被System bars擋住,通過fitsSystemWindows就可以解決這個問題,下面就著重講一下fitsSystemWindows。

5 fitsSystemWindows的作用

大部分情況下,都不會在System windows下面顯示內容,但是在實現沉浸式的效果時會導致在System windows下面顯示內容,因此需要確保可交互控件(比如按鈕)不要顯示在System windows下面。android:fitsSystemWindows=“true” 默認行為就是通過在 View 上設置和System windows一樣高度的邊框(padding )來確保你的content部分——Activity中setContentView()中傳入的就是content——不會與System windows重疊,因此android:fitsSystemWindows="true"的默認行為正好解決上面的情況。

以下是需要注意的地方:
1> fitsSystemWindows
fitsSystemWindows 按照深度優先的方式起作用的,因此如果一個 ViewGroup 使用了 inset (系統窗口的尺寸)則會導致其他與其相關的 View 受到影響。
2> Insets
始終相對于全屏幕
——Insets是相對于屏幕的,它決定著應用的Window的邊界與屏幕邊界的距離。因為Insets的生成在View layout之前就已經完成了,所以系統對于View長什么樣一無所知。
3> 其它padding將通通被覆蓋。需要注意,如果你對一個View設置了android:fitsSystemWindows="true",那么你對該View設置的其他padding將通通無效。

在絕大多數情況下,默認情況就已經夠用了。比如一個全屏的視屏播放器。如果你不想被System Bars遮住的話,那么在ViewGroup上設置fitsSystemWindows="true"即可。

或者,也許你希望你的RecyclerView能夠在透明的navigation bar 下面滾動。那么只需將android:fitsSystemWindows="true" android:clipToPadding="false"同時使用即可, 滾動的內容會繪制在navigation bar下面,同時當滾動到最下面的時候,最后一個item下面依舊會有padding,使其可以滾到navigation bar上方(而不是在navigation bar下面滾不上來!)。

譯者注:clipToPadding是ViewGroup的屬性。這個屬性定義了是否允許ViewGroup在padding中繪制,該值默認為true,即不允許。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容