關於 Android 中的各種 Bar 和“透明狀態列”的一些知識

sydMobile發表於2019-05-12

此篇文章主要講解關於沉浸式狀態列,程式全屏和分清狀態列、ActionBar、ToolBar 的一些知識內容。主要是講解如何適配狀態列。關於 ActionBar 和 ToolBar 的使用沒有細緻講解。

文章最早釋出於我的微信公眾號 Android開發者家園
本文為sydMobile原創文章,可以隨意轉載,但請務必註明出處! 由於個人精力有限,多個渠道釋出,排版可能會有問題。重點維護我的個人公眾號「 Android 開發者家園 」

其實這篇主要是講解適配狀態列的,在這其中可能有些讀者對狀態列(StatusBar)、ActionBar、ToolBar、TitleBar有點混淆或者感覺很混亂,所以就有了這其中的內容。

關於 TitleBar、ActionBar、ToolBar、StatusBar

首先強調一點 StatusBar 和前面這幾種 Bar 是完全的兩回事,其實 statusbar 應該寫成 status bar ,也就是我們的狀態列。沒錯就表示我們 Android 中最上面顯示時間、通知的那一欄。

說起 TitlBar、ActionBar、ToolBar,要和 Android 的發展歷史有關了。

在 Android 3.0 之前,設計上沒有美感之說,知識為了完成功能,這個時候最上面樣式就是顯示當前頁面的 Title,也就是 TitleBar,這個時代的 Android 機都是有物理返回鍵的。這個時候就是充當一個 Title 的作用,這也就是 TitleBar。它的樣子是這樣的

titlebar.png

上面顯示MyStudyDemo 的一欄就是 TitleBar 了。

然後在 Android 3.0 API 11 開始注重美感了,引進了 HOLO 樣式,這個時候用 ActionBar代替了 TitleBar 了。這個時候的 Bar 的功能不再僅僅限於展示標題了,有了很多的新功能。

actionbar.png

Fragment 也是在 Android 3.0 的時候引入的,所以 Android 3.0 對於 Android 的發展史還是很重要的。

最簡單的 ActionBar

簡單ActionBar.png

值得注意的是 TitleBar 和 ActionBar 在本質上是一樣的,他們都不是我們常用的控制元件的形式,而是繫結在 DecorView 中的,可以通過 getWindow().setFeatureInt() 暴露出的幾個方法進行修改功能和樣式。

正是因為 ActionBar 是巢狀在 DecorView 中的,它不是一個獨立的控制元件,而且由於國內的 Android 應用開發環境,一般都不遵循 Google 的那一套設計,所以運用起來很不靈活。隨後在 Android 5.0 推出了 ToolBar 控制元件,這是一個完全獨立的控制元件,你可以盡情的運用了。

2019-04-25_20-44-48.png

好了,到這裡來應該徹底分清楚這幾個 bar 了吧,這是 Android 的一個歷史發展形成的,可以結合我的這篇文章 blog.csdn.net/sydMobile/a… 來詳細的看一下 Android 在樣式上的發展。

狀態列的歷史以及更正錯誤叫法

因為上面也說了 ActionBar 是在 Android 3.0 以後才引用的,但是真正的規範是在 Android 4.1 以及以上。所以在 Android 4.1 以下版本是不支援 ActionBar 的。這裡針對 ActionBar 的操作全是在 Android 4.1 以上執行的,不適用於 Android 4.1 以下版本。

說明:在 Android 4.4 之前狀態列一直就是黑色的,在 Android 4.4 中帶來了 windowTranslucentStatus 這一特性,這個時候才可以給狀態列設定顏色。在國內將這種狀態列變色叫做沉浸式狀態列,其實這種叫法是錯誤的,但是時間久了,大家都這麼叫了,就不追究了,就把這種狀態列變色叫做沉浸式狀態列了(這也是由國內網際網路發展太過迅速,忽略了很多細節點,相關方面的人才缺乏,另一個方面是國內的 Android 開發環境造成的,碎片化太過嚴重,不同的手機廠商關於這些又有不同的叫法)。

關於沉浸式和透明式概念說明

在谷歌官方中:

在 Android 4.4 Google 引入了可以在閱讀電子書、玩遊戲、看電影時支援全屏模式(Immersive Mode 沉浸模式),同時也支援更改修改狀態列的顏色。

可以知道在官方是根本沒有 沉浸式狀態列 這種說法的。只有 沉浸模式 就是其實就是出於全屏狀態。所謂的 ”沉浸式狀態列“,類似於下面圖的樣子:

透明狀態列.png

上面也說了這種錯誤的叫法是不對的,沒有沉浸式狀態列 ,這種樣式只是將內容 UI 設定成了全屏,把狀態列設定成了透明。所以這種是叫做 狀態列透明模式

設定透明狀態列

先來幾組效果圖,從效果 1 開始逐漸遞進演示,這裡面的幾個重要引數會在後面具體說明,這裡先說明現象

1.內容佈局全屏

在 Android 4.1 以上設定去除狀態列或者認為是狀態列被內容佈局遮擋了(全屏)和去除 ActionBar

View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 情況說明:採用這種方式全屏,如果你切換到別的應用程式,或者在通知欄下拉一下,狀態列就會下來,我們的 UI 佈局就會跑到狀態列下面,和沒有設定的時候效果是一樣的。
複製程式碼

效果圖:

screen.png

2.內容佈局全屏(被狀態列遮住頂部)
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 可以看到我們在 option 中新增了一個 STABLE 屬性,這個屬性是用阿里保持我們的 UI 檢視穩定的,使得它不會因為系統 UI 的變化而重新 layout 。記住這個屬性長和 FLAG_LAYOUT_XXX 這種屬性在一起使用。STABLE 就是會始終給系統 UI 保留一個空間(不管系統 UI 有沒有消失,並且懸浮在我們自己的 UI 檢視上面 )
// 可以看到這種效果,狀態列仍然還在,只是你仔細發現,原佈局有一部分被狀態列給覆蓋了,也就是說這種方式的話,佈局延伸到了狀態列。這個時候我們只要設定狀態列為透明。就是我們想要的透明狀態列的那種效果了。
複製程式碼

效果圖:

layout_fullscreen.png

3.由效果 2 變成透明狀態列模式
// 只需要在 2 的基礎上把狀態列設定成透明就可以了
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
if(Build.VERSION.SDK_INT >= 21){
    // 這個方法只有 5.0 後才可以用,也就是說 Android 5.0 以前不支援更改狀態列顏色,這是說的是原生系統
    // 小米的 ui 在 Android 5.0 之前也提供了修改方法
    getWindow.setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 這樣我們的狀態列就變成透明的了,就是人們常說的“沉浸式狀態列”
複製程式碼

layout_fullscreen_tran.png

4.屬性 STABLE 的作用
// 主要是為了驗證 STABLE 的作用,在 1 的案例中,我們只用 FLAG_FULLSCREEN 屬性,會發現在切換應用或者觸碰系統 UI 的時候,這個時候我們的狀態列就會出來,然後我們的 UI 佈局就會“被壓在”狀態列的下面(實際上是把我們的佈局重新 layout 了)然後如果新增 STABLE 屬性呢?
if (Build.VERSION.SDK_INT >= 21){
    View decorView = getWindow().getDecorView();
    int option = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    decorView.setSystemUiVisibility(option);
    getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 結果就是 提前預留了一塊系統 UI 的控制元件,下拉的時候,狀態列內容就會顯示出來。(這樣我們的 UI 佈局是沒有被重新 layout 的)

複製程式碼

screnn_stable.png

5.沉浸模式
// 所謂沉浸模式就是一開始我們的 UI 佈局是全屏的,狀態列和虛擬導航鍵也是隱藏的,當我們需要的系統 UI 的時候,從狀態列的位置下拉就可以出現系統 UI,這個時候就需要一個新的屬性了 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 需要在 Android 4.4 及其以上版本
	@Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && Build.VERSION.SDK_INT >=19){
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility( 
                     View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            );

        }
    }
// 當觸控底部或者上部狀態列的時候,導航欄和狀態列就會出來,過一會就會消失,類似於遊戲中那樣。
複製程式碼

immersive_sticky_all.png

最後,總結這幾個 FLAG 的作用
  • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:作用就是使我們的 UI 佈局可以延伸到狀態列,狀態列懸浮會遮擋住我們的 UI 佈局,和 View.SYSTEM_UI_FLAG_LAYOUT_STABLE 一起使用
  • View.SYSTEM_UI_FLAG_FULLSCREEN:作用使 UI 佈局延伸到狀態列,全屏顯示,狀態列消失,下拉的時候,狀態列依然會出現,並且不再消失。
  • View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:沉浸模式,配合 View.SYSTEM_UI_FLAG_FULLSCREEN 和 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 來實現
  • View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION :作用就是使我們的 UI 佈局可以延伸到導航欄,導航欄懸浮會遮擋住我們的 UI 佈局。
  • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:作用使 UI 佈局延伸到導航欄,全屏顯示,導航欄消失。單獨使用的話,觸碰 UI 任何地方,系統都會將這個屬性設定去除,不再生效因此需要和 View.SYSTEM_UI_FLAG_IMMERSIVE 配合使用
  • View.SYSTEM_UI_FLAG_IMMERSIVE:單獨使用是沒有任何意義的,需要和 View.SYSTEM_UI_FLAG_FULLSCREEN、View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 配合使用,當出現隱藏的系統欄的時候,之前的所有設定就會被清除。

修改狀態列顏色

Android 5.0 (API 21) 後支援直接修改狀態列的顏色,在 Android 4.4(API 19)之前是不允許操作狀態列的,也就是說在 Android 4.4 之前,我們是沒法對狀態列進行任何操作的。

Android 4.4 修改狀態列顏色
// 將狀態列設定為透明(需要 API 19) 設定成這種模式後,狀態列會變成透明,我們的內容佈局(只是我們 Activity 對應的佈局,不包含 Window 中的 ActionBar)會佔據系統欄。類似於 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | LAYOUT_STABLE); getWindow().setStatusBarColor(Color.TRANSPARENT);

// 首先設定狀態列為透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

// 這個時候,我們的佈局內容中佔據系統欄了,這個時候,我們只需要自己來建立一個系統欄背景就可以了。
View mStatusBarView = new View(context);
int screenWidth = 螢幕寬度;
int statusBarHeight = 狀態列高度(是可以獲取的);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(screenWidth,statusBarHeight);
mStatusBarView.setLayoutParams(params);
mStatusBarView.requestLayout();
// 獲取系統預設的根佈局
ViewGroup systemContent = findViewById(android.R.id.content);
// 這個就是我們自己的佈局檔案
ViewGroup userContent = systemContent.getChildAt(0);
// 新增到我們的佈局中
userContent.addView(mStatusBarView,0);

複製程式碼
Android 5.0 修改狀態列顏色
// 方法一  
getWindow().setStatusBarColor(int color );
// 方法二
樣式中: <item name="colorPrimaryDark">@color/colorAccent</item> 對應狀態列顏色
複製程式碼

修改狀態列文字顏色

關於狀態列的文字顏色,是在 Android 6.0 才開始可以支援修改的

// 修改成 黑色字型
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

複製程式碼

setFitsSystemWindows

setFitsSystemWindows(boolean is);
在直接呼叫這個方法的時候,只有我們自己的根佈局(在 mContentParentParent 中,titleBar 的位置是固定的),呼叫才起作用。表示是否保留系統欄的空間。可以在佈局屬性中設定。作用和給控制元件新增 "fitsSystemWindows" 屬性相同。
如果設定了這個屬性為 true,那麼則是保留系統 UI 的位置(實際上是固定了我們的 UI 的高度,我們 UI 的高度就是螢幕去掉系統欄高度後的高度),那麼這個時候你如何設定了 FLAG_LAYOUT_HIDE_NAVIGATION 是不起作用的,因為我們的佈局高度已經確定了,不可能延伸到系統欄。
複製程式碼

設定佈局完全全屏的方式:

// 方式一
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 方式二
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
// 方式三 style.xml 中配置
<style name = "fullScreen" parent="Theme">
	<item name="android.windowFullscreen">true</item>
</style>

// 總結:
關於 addFlags 和改變 statusbar 顏色的方法在 getWindow 裡面
複製程式碼

頁面佈局

為了說明頁面的佈局關係,這裡引入一張圖片(來自網上)

Android頁面來自網路.png

每個 Activity 對應一個頁面,是不包括 status bar 的,不過可設定 status bar 顯示還是不顯示,可以設定 Activity 是否延伸到 status bar 的位置(確切的來說是 Window 視窗)

DecorView 是繼承自 FrameLayout 的。

其中 mContentParent 的 id 是 com.android.internal.R.id.content

mContentParent 通過這個 id 就可以獲取到我們自己的佈局的根佈局了。

titleBar 也有一個 id

相關文章:yifeng.studio/2017/02/19/…

更多資源請檢視個人公眾號

歡迎大家關注我的微信公眾號,和我交流分享

相關文章