Android沉浸式全屏講解(狀態欄、導航欄處理)

Android應用中經常會有一些要求全屏顯隱狀態欄導航欄的需求。通過全屏沉浸式的處理可以讓應用達到更好的顯示效果。下面系統的講解一下有關全屏,隱藏狀態欄導航欄,沉浸式的知識。

在Android4.1之前只能隱藏狀態欄,在Android4.1以及之后Android提供了一套控制SystemUI的方式,重點放在第二部分。

在Android4.1之前隱藏狀態欄

在Android4.1之前你可以通過設置WindowManager來隱藏狀態欄。你可以通過編碼來實現,也可以通過在manifest文件中給activity設置theme來實現。
如果你要設置activity的狀態欄一直處于隱藏狀態,那么在manifest設置的方式是你的首選方式(盡管也可以使用編程方式。)。實現方式如下。

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

使用manifest設置的方式有以下優點:

  1. 比編程方式更加容易維護,更不容易出錯。

  2. 會有一個更加平滑的過渡,因為在實例化activity之前系統就已經擁有了呈現UI所需的信息。

另外,也可以以編程方式設置WindowsManager標志。這種方法使用戶在與應用程序交互時更容易隱藏和顯示狀態欄。

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // If the Android version is lower than Jellybean, use this call to hide
        // the status bar.
        if (Build.VERSION.SDK_INT < 16) {
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
        setContentView(R.layout.activity_main);
    }
    ...
}

當你設置WindowsManager標志時(無論是通過theme還是以編程方式),除非您的應用程序清除了這些標志,否則這些標志將保持有效。

可以使用FLAG_LAYOUT_IN_SCREEN將activity布局設置為使用相同的屏幕區域。當你啟用FLAG_FILLSCREEN時,可以使用相同的屏幕區域。這將防止在狀態欄隱藏和顯示時調整內容的大小。

設置全屏

從這里開始才是本文的重點。

在Android4.1以及更高版本可以使用setSystemUiVisibility來控制SystemUI,為了更系統的講解,不分別按照效果來講,而是把用到的flag先列出來一起講。以下flag經過互相組合能達到全屏隱藏狀態欄,全屏隱藏導航欄,全屏顯示狀態欄導航欄等很多不同效果。

控制SystemBar相關:

  • SYSTEM_UI_FLAG_FULLSCREEN

  • SYSTEM_UI_FLAG_HIDE_NAVIGATION

  • SYSTEM_UI_FLAG_LOW_PROFILE

布局相關:

  • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  • SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  • SYSTEM_UI_FLAG_LAYOUT_STABLE

沉浸式相關 (4.4 引入):

  • SYSTEM_UI_IMMERSIVE

  • SYSTEM_UI_IMMERSIVE_STICKY

控制 SystemBar 相關

SYSTEM_UI_FLAG_FULLSCREEN

該屬性是用來隱藏狀態欄的。

View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
getSupportActionBar().hide();

通過以上代碼可以實現隱藏狀態欄。為了顯示出全屏效果,建議同時將ActionBar隱藏掉。僅設置這一條屬性,顯示效果存在以下特性。

  • 當滑動system bar、點擊home鍵menu鍵就會清除掉flag,狀態欄會重新顯示出來。

  • 并且布局也會隨著狀態欄的顯隱進行布局調整進行重繪。

image

效果圖

SYSTEM_UI_HIDE_NAVIGATION

該屬性是用來隱藏導航欄的

View decorView2 = getWindow().getDecorView();
int uiOptions2 = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView2.setSystemUiVisibility(uiOptions2);
getSupportActionBar().hide();

以上代碼可以實現隱藏狀態欄和導航欄。
盡管可以僅僅隱藏導航欄,但是建議隱藏導航欄的時候將狀態欄一并隱藏,以達到更好的沉浸效果。

  • 與隱藏狀態欄不同的是點擊任意布局中的任意位置都會導致導航欄導航欄重新顯示出來。

  • 并且布局也會隨著狀態欄導航欄的顯隱進行布局調整進行重繪。

image

SYSTEM_UI_LOW_PROFILE

這個屬性的能力是讓SystemBar在視覺上變得模糊,重要性變得更低一點。具體表現是狀態欄圖標僅保留電量時間關鍵圖標,并且變暗。導航欄圖標變成三個點或者變暗。這個flag使用的很少。

View decorView7 = getWindow().getDecorView();
int uiOptions7 = View.SYSTEM_UI_FLAG_LOW_PROFILE;
decorView7.setSystemUiVisibility(uiOptions7);
image

布局相關

在新的Android4.1以及之后新的SystemUI設置里,僅單獨設置隱藏狀態欄和導航欄的flag會導致布局重繪,為了在顯隱狀態欄和導航欄的時候保持布局的穩定的顯示效果,就需要以下屬性了。

SYSTEM_UI_FLAG_LAYOUT_STABLE

該flag的作用是保持布局穩定,避免在顯隱狀態欄導航欄的時候發生布局的變化。可以輔助以下SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION兩個flag的使用,讓應用保持全屏的情況下,布局不隨狀態欄導航欄顯隱發生變化。也可以不配合這兩個flag使用,也能達到保持布局穩定的效果,不過不能實現全屏,會留出狀態欄和導航欄的位置。

View decorView8 = getWindow().getDecorView();
int uiOptions8 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView8.setSystemUiVisibility(uiOptions8);
getSupportActionBar().hide();

以上代碼設置了隱藏狀態欄和SYSTEM_UI_FLAG_LAYOUT_STABLE兩個flag,顯示效果為隱藏狀態欄,布局穩定。但是布局不延伸到全屏,效果看起來還是很奇葩的。

  • 當滑動systembar、點擊home鍵多任務鍵就會清除掉flag。狀態欄會重新顯示出來。

  • 布局不會隨著狀態欄的顯隱進行調整變化。

image
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

可以讓布局延伸到狀態欄的位置。在狀態欄在隱藏和顯示之前切換的時候,布局穩定的顯示在狀態欄后面(如果顯示狀態欄則布局在狀態欄后面,隱藏狀態欄布局也不變)。

View decorView3 = getWindow().getDecorView();
int uiOptions3 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView3.setSystemUiVisibility(uiOptions3);
getSupportActionBar().hide();

以上代碼顯示出來的效果和上一段代碼相比,布局延伸到了狀態欄的位置。

  • 當滑動systembar、點擊home鍵menu鍵就會清除掉flag。狀態欄會重新顯示出來。

  • 布局不會隨著狀態欄的顯隱進行調整變化

image
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

可以讓布局延伸到導航欄的位置。可以讓導航欄在隱藏和顯示之前切換的時候,布局穩定的顯示在導航欄后面(如果顯示導航欄則在導航欄后面,隱藏導航欄也不變)。

View decorView4 = getWindow().getDecorView();
int uiOptions4 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView4.setSystemUiVisibility(uiOptions4);
getSupportActionBar().hide();

以上代碼的顯示效果是狀態欄和導航欄隱藏,布局延伸到了狀態欄和導航欄的位置。

  • 點擊任意布局就會清除掉flag。狀態欄導航欄會重新顯示出來。

  • 布局不會隨著狀態欄導航欄的顯隱進行調整變化。

image

在設置了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION flag的情況,如果把狀態欄導航欄顏色設置為透明,則會有透明的狀態欄導航欄覆蓋在布局上的效果。這也證明了即便布局狀態欄導航欄出來了,布局也確實延伸到了狀態欄導航欄的位置。

沉浸式相關

以上flag的組合設置中一直存在一個問題(在點擊Home鍵、menu鍵等操作會導致flag被清除,導航欄一點擊界面就會導致flag被清除,效果消失的問題。)。其實我們大部分情況都希望效果能夠穩定的顯示,而不是在簡單操作之后就會消失掉。下面兩個屬性就是為這個問題工作的。

SYSTEM_UI_IMMERSIVE

在以上flag設置的基礎上設置該屬性,可以保證在點擊home鍵、menu鍵時不會失去狀態。但是如果手動調出systembar的時候,設置的相關flag還是會被清除掉。

View decorView5 = getWindow().getDecorView();
int uiOptions5 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_IMMERSIVE;
decorView5.setSystemUiVisibility(uiOptions5);
getSupportActionBar().hide();

以上代碼的顯示效果。

  • 隱藏狀態欄、導航欄。

  • 布局延伸到了狀態欄、導航欄的位置。

  • 布局穩定顯示,不會因為狀態欄的顯隱來調整布局。

  • 當手動調出狀態欄導航欄的時候,flag才會被清除。

image
SYSTEM_UI_IMMERSIVE_STICKY

設置這個屬性后。當狀態欄隱藏的時候,手動調出狀態欄導航欄,顯示一會兒隨后就會隱藏掉。設置該屬性后不會清除flag,該屬性是比較常用的一種。但是離開頁面肯定是會導致flag被清除掉的,以上所有flag設置都會有這種情況。

View decorView6 = getWindow().getDecorView();
int uiOptions6 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView6.setSystemUiVisibility(uiOptions6);
getSupportActionBar().hide();

以上代碼的顯示效果。

  • 隱藏狀態欄、導航欄

  • 布局延伸到了狀態欄、導航欄的位置。

  • 布局穩定顯示,不會因為狀態欄的顯隱來調整布局。

  • 手動調出的狀態欄導航欄會半透明顯示覆蓋在界面上,隨后還會隱藏掉。

  • 如果離開頁面還是會導致flag被清除,效果消失。

image

對于flag被清除問題,重設的位置可以放在onWindowFocusChanged中。

想要清除flag可以直接調用setSystemUiVisibility(0);

小總結

有了以上flag的總結,我們可以實現我們想要的一些效果。比如全屏隱藏狀態欄導航欄、全屏顯示導狀態欄導航欄、全屏顯示狀態欄隱藏導航欄、全屏隱藏狀態欄顯示導航欄,甚至組合出非全屏但是隱藏狀態欄導航欄等一些奇葩的顯示效果。下面用一段代碼展示一下全屏中的各中顯示效果,也是我們項目常用的效果。

public void setFullscreen(boolean isShowStatusBar, boolean isShowNavigationBar) {
    int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

    if (!isShowStatusBar) {
        uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
    }
    if (!isShowNavigationBar) {
        uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    }
    getWindow().getDecorView().setSystemUiVisibility(uiOptions);

    //隱藏標題欄
    getSupportActionBar().hide();
    //專門設置一下狀態欄導航欄背景顏色為透明,凸顯效果。
    setNavigationStatusColor(Color.TRANSPARENT);
}

public void setNavigationStatusColor(int color) {
    //VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
    if (Build.VERSION.SDK_INT >= 21) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        getWindow().setNavigationBarColor(color);
        getWindow().setStatusBarColor(color);
    }
}
image
image
image
image

監聽狀態欄導航欄的變化

有時候我們想知道狀態欄、導航欄的顯隱變化,便于我們自定義一些操作。Android也提供了一個監聽變化的能力。

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.
        }
    }
});

上面代碼和注釋可以說寫的很清楚了。可以在onSystemUiVisibilityChange接口中通過判斷LOW_PROFILE,HIDE_NAVIGATION和FULLSCREEN的值來看顯隱,0為顯示,1為隱藏。

最后

如果閱讀過程中發現有什么問題,歡迎交流指正。打完,收工!

image

歡迎關注~~~
歡迎關注【Funny新青年】微信公眾號~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容