背景
在我做 Android 開發(fā)之前戴已,我就發(fā)現(xiàn)有些 App 的狀態(tài)欄和導(dǎo)航欄有透明效果蹲缠,或者是沉浸式效果,比如說酷安的客戶端,是像這個樣子的
雖然只是簡單的改變契沫,但相對于傳統(tǒng)的上下兩個黑條來說采蚀,視覺效果會美觀很多,我當(dāng)時挺糾結(jié)很多主流應(yīng)用沒有這種效果,還特意安裝了一個 xposed 框架的模塊來強制實現(xiàn)沉浸式狀態(tài)欄和導(dǎo)航欄瓶摆,不過貌似那個模塊會影響性能,從那時我就決定性宏,如果將來我做 Android 開發(fā)群井,一定會讓我開發(fā)的應(yīng)用都使用這種效果,如今終于實現(xiàn)啦毫胜!
開源庫
經(jīng)過對大量應(yīng)用的觀察书斜,我發(fā)現(xiàn)這種透明狀態(tài)欄和導(dǎo)航欄或者叫沉浸式狀態(tài)欄和導(dǎo)航欄的效果主要有以下幾種:
1、自定義顏色的狀態(tài)欄和導(dǎo)航欄酵使;
2荐吉、半透明的狀態(tài)欄和導(dǎo)航欄;
3口渔、完全透明的狀態(tài)欄和導(dǎo)航欄(其實就是第二種的極限狀態(tài)样屠,我更喜歡 叫這種為沉浸式狀態(tài)欄和導(dǎo)航欄);
4缺脉、隱藏狀態(tài)欄和導(dǎo)航欄痪欲。
效果分別如下:
事實上,在 github 上也有不少關(guān)于這方面的開源項目攻礼,不過這些開源項目大多只是針對狀態(tài)欄實現(xiàn)了透明或者沉浸式的效果业踢,而對下方的導(dǎo)航欄并沒有做相應(yīng)的處理,于是我自己寫了一個針對狀態(tài)欄和導(dǎo)航欄都實現(xiàn)透明或者沉浸式的效果的開源庫礁扮,地址如下:
這里要特別說明一下知举,狀態(tài)欄和導(dǎo)航欄透明是在 Android 4.4 開始支持的,但是 Android 4.4 的實現(xiàn)原理和 Android 5.0 以上的實現(xiàn)原理并不一樣深员,這就導(dǎo)致如果在 Android 5.0 以上如果使用 Android 4.4 的實現(xiàn)方法會出現(xiàn)顯示效果不一致的問題负蠕,我寫的這個庫分別對 Android 4.4 和 Android5.0 以上做了處理,使它在不同的系統(tǒng)版本下顯示效果達(dá)到高度統(tǒng)一倦畅,使用這個庫遮糖,首先需要添加依賴:
compile 'org.zackratos:ultimatebar:1.0.3'
接下來對上面四種情況分別作介紹。
自定義顏色的狀態(tài)欄和導(dǎo)航欄
要設(shè)置自定義顏色的狀態(tài)欄和導(dǎo)航欄只需要在 onCreate 方法中調(diào)用如下代碼:
UltimateBar ultimateBar = new UltimateBar(this);
ultimateBar.setColorBar(ContextCompat.getColor(this, R.color.DeepSkyBlue));
那么他的內(nèi)部是怎么實現(xiàn)的呢叠赐,查看源碼可以發(fā)現(xiàn)欲账,內(nèi)部源碼是這樣的:
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setColorBar(@ColorInt int color, int alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
int alphaColor = alpha == 0 ? color : calculateColor(color, alpha);
window.setStatusBarColor(alphaColor);
window.setNavigationBarColor(alphaColor);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
int alphaColor = alpha == 0 ? color : calculateColor(color, alpha);
ViewGroup decorView = (ViewGroup) window.getDecorView();
decorView.addView(createStatusBarView(activity, alphaColor));
if (navigationBarExist(activity)) {
decorView.addView(createNavBarView(activity, alphaColor));
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
setRootView(activity, true);
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setColorBar(@ColorInt int color) {
setColorBar(color, 0);
}
我們可以看到第一個方法里面?zhèn)魅肓藘蓚€參數(shù),第一個參數(shù)是自定義的顏色值芭概,第二個參數(shù)是顏色深度值赛不,最小為 0,最大為 255罢洲,當(dāng)深度值為 0 時踢故,狀態(tài)欄和導(dǎo)航欄的顏色就是第一個參數(shù)傳入的顏色值文黎,即為第二個方法中的情況;當(dāng)深度值不為 0 時殿较,會根據(jù)深度值計算得到最終的顏色值耸峭,然后設(shè)置到狀態(tài)欄和導(dǎo)航欄上面。
正如前面所說淋纲,這里分別針對 Android 4.4 和 Android 5.0 以上做了不同處理劳闹,首先來看 Android 5.0 以上的情況,事實上 Android 5.0 以上的實現(xiàn)非常簡單洽瞬,因為 Android 5.0 以上可以直接設(shè)置狀態(tài)欄和導(dǎo)航欄的顏色本涕,所以只需要先得到最終的顏色值,然后調(diào)用 setStatusBarColor 和 setNavigationBarColor 方法進(jìn)行設(shè)置就好了伙窃。然后 Android 4.4 稍微麻煩一點菩颖,首先必須要添加 FLAG_TRANSLUCENT_STATUS 這個 flag 來把狀態(tài)欄設(shè)置為透明,然后再在狀態(tài)欄上面添加一個 view 來保證狀態(tài)欄的顏色对供,然后再調(diào)用 navigationBarExist 方法來判斷當(dāng)前手機是否存在導(dǎo)航欄位他,如果存在,對導(dǎo)航欄做同樣的處理产场,最后必須調(diào)用 setRootView 方法,這個方法是干嘛的呢舞竿,看一下它的代碼:
private void setRootView(Activity activity, boolean fit) {
ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content);
for (int i = 0, count = parent.getChildCount(); i < count; i++) {
View childView = parent.getChildAt(i);
if (childView instanceof ViewGroup) {
childView.setFitsSystemWindows(fit);
((ViewGroup)childView).setClipToPadding(fit);
}
}
}
可以看到京景,這個方法是用來設(shè)置布局的子 view 的 fitsSystemWindows 參數(shù)的,相當(dāng)于在布局中添加 android:fitsSystemWindows="true"骗奖,如果不調(diào)用這個方法确徙,就會導(dǎo)致布局中的內(nèi)容覆蓋到狀態(tài)欄和導(dǎo)航欄上面了。
半透明的狀態(tài)欄和導(dǎo)航欄
半透明狀態(tài)欄和導(dǎo)航欄的使用方法也非常簡單执桌,只要在 onCreate 方法中調(diào)用以下代碼:
UltimateBar ultimateBar = new UltimateBar(this);
ultimateBar.setTransparentBar(Color.BLUE, 50);
同樣的看一下它的內(nèi)部實現(xiàn)鄙皇,如下:
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setTransparentBar(@ColorInt int color, int alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
int finalColor = alpha == 0 ? Color.TRANSPARENT :
Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
window.setNavigationBarColor(finalColor);
window.setStatusBarColor(finalColor);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorView = (ViewGroup) window.getDecorView();
int finalColor = alpha == 0 ? Color.TRANSPARENT :
Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
decorView.addView(createStatusBarView(activity, finalColor));
if (navigationBarExist(activity)) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
decorView.addView(createNavBarView(activity, finalColor));
}
}
}
兩個參數(shù)分別表示顏色和透明度,透明度最小為 0仰挣,最大為 255伴逸,對于 Android 5.0 及以上,需要先添加 SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION膘壶,SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN错蝴,SYSTEM_UI_FLAG_LAYOUT_STABLE 三個 flag,以保證布局的內(nèi)容可以覆蓋到狀態(tài)欄和導(dǎo)航欄上面颓芭,然后同樣的調(diào)用 setStatusBarColor 和 setNavigationBarColor 方法來設(shè)置狀態(tài)欄和導(dǎo)航欄顏色顷锰,不過這里的顏色都是經(jīng)過計算的半透明的顏色,對于 Android 4.4亡问,跟之前的自定義顏色一樣官紫,首先需要添加 FLAG_TRANSLUCENT_STATUS 這個 flag 保證狀態(tài)欄透明,然后再在狀態(tài)欄上添加一個半透明的 view,然后調(diào)用 navigationBarExist 方法判斷導(dǎo)航欄是否存在束世,如果存在酝陈,也做相同的處理,這里要注意良狈,因為半透明狀態(tài)欄和導(dǎo)航欄需要布局內(nèi)容覆蓋到狀態(tài)欄和導(dǎo)航欄上面的效果后添,所以在這里不能調(diào)用 setRootView 方法。
完全透明的狀態(tài)欄和導(dǎo)航欄
其實完全透明的狀態(tài)欄和導(dǎo)航欄就是半透明的狀態(tài)欄和導(dǎo)航欄中當(dāng)透明度為 0 的情況薪丁,只需在 onCreate 方法中調(diào)用如下方法:
UltimateBar ultimateBar = new UltimateBar(this);
ultimateBar.setImmersionBar();
查看它的內(nèi)部實現(xiàn)可以發(fā)現(xiàn)它是這么調(diào)用的:
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setImmersionBar() {
setTransparentBar(Color.TRANSPARENT, 0);
}
就是半透明狀態(tài)欄和導(dǎo)航欄的特殊情況遇西,不做過多介紹了。
隱藏狀態(tài)欄和導(dǎo)航欄
這種情況比較常見了严嗜,一般玩游戲粱檀,看視頻就是這種效果,這種效果的實現(xiàn)有點特殊漫玄,必須重寫 Activity 的 onWindowFocusChanged 方法茄蚯,如下:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
UltimateBar ultimateBar = new UltimateBar(this);
ultimateBar.setHintBar();
}
}
它的內(nèi)部實現(xiàn)也比較簡單,如下:
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setHintBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
View decorView = activity.getWindow().getDecorView();
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);
}
}
就是添加幾個 flag睦优,這是固定寫法渗常,也不做過多介紹了。
針對 DrawerLayout 的實現(xiàn)
還有一種特殊情況汗盘,就是對于 DrawerLayout皱碘,上面的方法會出現(xiàn)一些問題,達(dá)不到想要的效果隐孽,這里針對 DrawerLayout 做了特殊處理癌椿,一般來說,對于 DrawerLayout 只要實現(xiàn)自定義顏色的狀態(tài)欄和導(dǎo)航欄效果就好了菱阵,其他情況就不用考慮了踢俄,可以在 onCrate 調(diào)用如下代碼:
UltimateBar ultimateBar = new UltimateBar(this);
ultimateBar.setColorBarForDrawer(ContextCompat.getColor(this, R.color.DeepSkyBlue));
但是這樣其實還是不夠的,還必須要在布局文件中在 DawerLayout 的子 view 的主界面添加 android:fitsSystemWindows="true"晴及,就像這樣:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/SpringGreen"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
注意都办,這里是在 DawerLayout 下面的主界面添加,DawerLayout 本身以及它下面的抽屜都不能添加抗俄,原因后面會說明脆丁,然后同樣看一下內(nèi)部實現(xiàn):
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setColorBarForDrawer(@ColorInt int color, int alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
ViewGroup decorView = (ViewGroup) window.getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
if (navigationBarExist(activity)) {
option = option | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
decorView.setSystemUiVisibility(option);
window.setNavigationBarColor(Color.TRANSPARENT);
window.setStatusBarColor(Color.TRANSPARENT);
int alphaColor = alpha == 0 ? color : calculateColor(color, alpha);
decorView.addView(createStatusBarView(activity, alphaColor), 0);
if (navigationBarExist(activity)) {
decorView.addView(createNavBarView(activity, alphaColor), 1);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorView = (ViewGroup) window.getDecorView();
int alphaColor = alpha == 0 ? color : calculateColor(color, alpha);
decorView.addView(createStatusBarView(activity, alphaColor), 0);
if (navigationBarExist(activity)) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
decorView.addView(createNavBarView(activity, alphaColor), 1);
}
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setColorBarForDrawer(@ColorInt int color) {
setColorBarForDrawer(color, 0);
}
這里稍微有點復(fù)雜,參數(shù)的傳遞和前面是一樣的动雹,就不多解釋了槽卫,對于 Android 5.0 以上的情況,首先添加前面兩個 flag 保證布局內(nèi)容能夠覆蓋到狀態(tài)欄上面胰蝠,然后判斷是否存在導(dǎo)航欄歼培,如果存在震蒋,再添加第三個 flag 保證布局內(nèi)容可以覆蓋到導(dǎo)航欄上面,然后狀態(tài)欄和導(dǎo)航欄都設(shè)為透明色躲庄,此時相當(dāng)于上面的完全透明的狀態(tài)欄和導(dǎo)航欄查剖,最后再分別在狀態(tài)欄和導(dǎo)航欄上面添加一個 view 保證狀態(tài)欄和導(dǎo)航欄有顏色,這樣就既保證了 DrawerLayout 可以覆蓋到狀態(tài)欄和導(dǎo)航欄上面噪窘,又保證了 DrawerLayout 下面的主布局內(nèi)容不會覆蓋到狀態(tài)欄和導(dǎo)航欄上面笋庄,最后的效果就是抽屜的內(nèi)容是覆蓋到狀態(tài)欄和導(dǎo)航欄上面的,而住布局的內(nèi)容不會覆蓋到狀態(tài)欄和導(dǎo)航欄的上面倔监,然后對于 Android 4.4直砂,其實和前面正常情況的設(shè)置自定義顏色的狀態(tài)欄和導(dǎo)航欄是一樣的,只是這里沒有調(diào)用 setRootView 方法浩习,而是在 DrawerLayout 下面的主布局中添加了 android:fitsSystemWindows="true"静暂,同樣實現(xiàn)了抽屜的內(nèi)容可以覆蓋到狀態(tài)欄和導(dǎo)航欄上面,而主布局的內(nèi)容不會覆蓋到狀態(tài)欄和導(dǎo)航欄上面谱秽,最后的效果如下圖
大致內(nèi)容也就這么多了洽蛀,最后再把這個庫的地址貼一遍:
如果覺得這個庫對你的開發(fā)有幫助,歡迎 star疟赊,歡迎 fork郊供,如果發(fā)現(xiàn)有什么問題或者有什么修改建議,歡迎反饋近哟,謝謝颂碘!
最后的最后,我最近在找工作椅挣,發(fā)現(xiàn)好多公司都太坑,有沒有哪位大俠可以幫我內(nèi)推一下塔拳,地點不限鼠证,我將不甚感激!