在以往看到關(guān)于沉浸式狀態(tài)欄的相關(guān)的內(nèi)容總是無從下手,不知道從哪看起,即使是實(shí)現(xiàn)了功能也是從網(wǎng)上找一些方案,潦草了事,本著不會就學(xué)的態(tài)度還是好好看了一下關(guān)于這個方面的知識,下面我們就系統(tǒng)的看一下如何實(shí)現(xiàn)沉浸式狀態(tài)欄
1.沉浸式狀態(tài)欄是通過什么方式實(shí)現(xiàn)的
沉浸式狀態(tài)欄是聽著非常高大上,但是他們實(shí)現(xiàn)方式非常簡單,
activity.getWindow().getDecorView().setSystemUiVisibility(uiFlags);
就是通過生面這一行代碼來實(shí)現(xiàn)的沉浸式狀態(tài)欄,從上面就能看出來想要實(shí)現(xiàn)沉浸式的效果就是設(shè)置多個flags,我們只要每一個flag的意義,并且知道他生效的版本,就能實(shí)現(xiàn)我們想要的效果
2.實(shí)現(xiàn)沉浸式狀態(tài)欄我們都需要解決那些問題,
1.適配劉海屏
在Android版本28的時(shí)候,增加了劉海屏,同時(shí)在Window.LayoutParams下增加了layoutInDisplayCutoutMode這個屬性,這個屬性有三個值分別
默認(rèn)情況洲押,全屏頁面不可用劉海區(qū)域,非全屏頁面可以進(jìn)行使用
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
允許頁面延伸到劉海區(qū)域
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;
不允許使用劉海區(qū)域
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
通過上面的介紹我們就知道應(yīng)該是第二個
那么適配劉海屏的方案就非常簡單了
public static void fitsNotchScreen(Window window) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams lp = window.getAttributes();
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
window.setAttributes(lp);
}
}
2.沉浸式狀態(tài)欄
在實(shí)現(xiàn)沉浸式的過程中我們想要實(shí)現(xiàn)的效果是將View延伸到狀態(tài)欄的下面,同時(shí)將狀態(tài)欄的背景色設(shè)置為透明,這就需要了解activity.getWindow().getDecorView().setSystemUiVisibility 所涉及到關(guān)于狀態(tài)欄的Flag的含義和作用
1.低調(diào)模式 隱藏不重要的圖標(biāo)
View.SYSTEM_UI_FLAG_LOW_PROFILE
2.隱藏底部導(dǎo)航欄,有操作后就會顯示出來
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
3.隱藏狀態(tài)欄汰寓,從狀態(tài)欄位置下拉重新出現(xiàn)
View.SYSTEM_UI_FLAG_FULLSCREEN
4.將布局內(nèi)容拓展到導(dǎo)航欄的后面
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
5.在不隱藏StatusBar的情況下,將view所在window的顯示范圍擴(kuò)展到StatusBar下面咧擂。
本次方案就使用了這個屬性
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
6.穩(wěn)定布局乍丈,需要配合SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN使用,
同時(shí)設(shè)置布局的android:fitsSystemWindows屬性咸这。
本次就是使用的這個方案,在StatusBarUtils中聲明了SYSTEM_UI_FLAG_LAYOUT_STABLE,
然后沉浸式的時(shí)候添加 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
7.配合SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN使用佳吞,
使?fàn)顟B(tài)欄和導(dǎo)航欄真正的進(jìn)入沉浸模式拱雏。
點(diǎn)擊屏幕任意區(qū)域,不會退出全屏模式底扳,
只有用戶上下拉狀態(tài)欄或者導(dǎo)航欄時(shí)才會退出铸抑。
View.SYSTEM_UI_FLAG_IMMERSIVE
8.效果同上,
當(dāng)用戶上下拉狀態(tài)欄或者導(dǎo)航欄時(shí)衷模,
這些系統(tǒng)欄會以半透明的狀態(tài)顯示鹊汛,
并且在一段時(shí)間后消失。
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
9設(shè)置狀態(tài)欄的顏色阱冶,6.0版本以后有效刁憋。 本方案中就使用了這個屬性
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
在仔細(xì)閱讀了上面的介紹我想你肯定還是有一些疑惑的,我們就從兩個方面來說,
1.游戲中的SystemUiFlag是如何使用
游戲沉浸式的效果絕大多數(shù)都是全屏,在手指在狀態(tài)欄下滑時(shí),顯示出狀態(tài)欄,通過上面的介紹就選用的Flags怎么組合呢,
int flags=View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN
這就是我們實(shí)現(xiàn)的效果
2.沉浸式狀態(tài)欄SystemUiFlag是如何使用的
int flags=View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
使用上面兩個就能實(shí)現(xiàn)趁寢式的效果,他們非常帶表的含義就是 讓狀態(tài)欄始終顯示在屏幕上,同時(shí)讓View 延伸到狀態(tài)欄的底部
那么最終實(shí)現(xiàn)沉浸式狀態(tài)欄的代碼就非常簡單了
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static int initBarAboveLOLLIPOP(Activity activity, int uiFlags) {
try {
uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;//將布局內(nèi)容拓展到狀態(tài)欄的后面
Window mWindow = activity.getWindow();
int defaultNavigationBarColor = mWindow.getNavigationBarColor();
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//判斷是否存在導(dǎo)航欄 如果導(dǎo)航欄存在移出導(dǎo)航欄透明效果,
if (getNavigationBarHeight(activity)>0) {
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
//需要設(shè)置這個才能設(shè)置狀態(tài)欄和導(dǎo)航欄顏色
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//設(shè)置狀態(tài)欄顏色
mWindow.setStatusBarColor(Color.TRANSPARENT);
//回復(fù)默認(rèn)導(dǎo)航欄顏色
mWindow.setNavigationBarColor(defaultNavigationBarColor);
}catch (Exception e){
}
return uiFlags;
}
這回再看上面的代碼就非常簡單了,同時(shí)也能猜到傳遞進(jìn)來的flag肯定是包含View.SYSTEM_UI_FLAG_LAYOUT_STABLE 這個屬性的
3.如果是深色的狀態(tài)欄,是否修改底部導(dǎo)航欄
// dark 的含義是否使用深色狀態(tài)欄字體和圖標(biāo)
public static int setNavigationIconDark(int uiFlags,boolean dark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && dark) {
return uiFlags | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;//設(shè)置黑色的底部導(dǎo)航欄
} else {
return uiFlags;
}
}
這里的設(shè)置就非常簡單了,沒有什么需要介紹的
4.是否使用深色狀態(tài)欄字體和圖標(biāo)
// dark 的含義是否使用深色狀態(tài)欄字體和圖標(biāo)
public static int setAndroidNativeLightStatusBar(int uiFlags, boolean dark) {
if (dark) {
return uiFlags | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;///意思是使用淺色狀態(tài)欄,那么就是使用黑色字體和黑色圖標(biāo)
} else {
return uiFlags;
}
}
5.適配小米和魅族中特殊機(jī)型
這里的話就是由于小米6和魅族一些特殊的機(jī)型他們使用的修改后的系統(tǒng),原生的方法不生效,所以在最后需要對他做特殊處理,在小米和魅族的官網(wǎng)就能找到,這里就不多說了
到了這里Api 21以上整個實(shí)現(xiàn)沉浸式狀態(tài)欄的過程我們就串聯(lián)起來了,所有的方法也都貼出來了,最后就是將他們串聯(lián)起來
public static void fitStatusBar(Activity activity,boolean isDarkMode){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
Window window = activity.getWindow();
//始終顯示狀態(tài)欄
int uiFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
//適配劉海屏
LollipStatusBarUtils.fitsNotchScreen(window);
//修改狀態(tài)欄顏色,同時(shí)將flag增加 將布局內(nèi)容拓展到狀態(tài)欄的后面 ,這里也可以修改底部導(dǎo)航欄顏色
uiFlags=LollipStatusBarUtils.initBarAboveLOLLIPOP(activity,uiFlags);
//設(shè)置深色底部導(dǎo)航欄 即黑白圖標(biāo)
uiFlags = LollipStatusBarUtils.setNavigationIconDark(uiFlags,isDarkMode);
///修改字體顏色
uiFlags=AndroidStatusbarTextFontUtils.setAndroidNativeLightStatusBar(uiFlags,isDarkMode);
//修改狀態(tài)欄可見性
window.getDecorView().setSystemUiVisibility(uiFlags);
///修改小米6和魅族部分系統(tǒng)的字體顏色
LollipStatusBarUtils.setOtherStatusBarLightMode(activity.getWindow(), isDarkMode);
}else{//19-20 只能添加控件,并修改背景顏色
KitKatStatusBar.initBarBelowLOLLIPOP(activity,isDarkMode);
}
}
}
Api 19-20沉浸式的實(shí)現(xiàn),
由于Api 19-20 我們無法改變狀態(tài)欄字體和圖標(biāo)的顏色,所以就需要我們使用一種另類的方式去實(shí)現(xiàn),他的實(shí)現(xiàn)思路大致思路是先讓View延伸到狀態(tài)欄下面,同時(shí)創(chuàng)建一個狀態(tài)欄高度的View,只要修改這個View的顏色,在深色時(shí)添加的顏色是#33000000,淺色時(shí)是透明,就可以了,
具體代碼
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static void initBarKitKat(Activity activity,boolean isDarkMode) {
//透明狀態(tài)欄
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//創(chuàng)建一個假的狀態(tài)欄
setStatusBarView(activity,isDarkMode);
//底部導(dǎo)航欄也需要設(shè)置,這里不管
}
/**
* 設(shè)置一個可以自定義顏色的狀態(tài)欄
*/
public static void setStatusBarView(Activity mActivity,boolean isDarkMode) {
View statusBarView = mActivity.getWindow().getDecorView().findViewById(R.id.serviceui_status_bar_view);
if (statusBarView == null) {
statusBarView = new View(mActivity);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
StatusBarUtils.getStatusBarHeight());
params.gravity = Gravity.TOP;
statusBarView.setLayoutParams(params);
statusBarView.setVisibility(View.VISIBLE);
statusBarView.setId(R.id.serviceui_status_bar_view);
((ViewGroup)mActivity.getWindow().getDecorView()).addView(statusBarView);
}
///這里需要調(diào)試如何修改顏色, 一般這里使用帶有透明度的灰色,因?yàn)樽煮w顏色不可以改變,所以這里比較好適配
if(isDarkMode){
statusBarView.setBackgroundColor(ContextCompat.getColor(mActivity,R.color.serviceui_status_bar_bg));
}else{
statusBarView.setBackgroundColor(Color.TRANSPARENT);
}
}
到了這里所有的實(shí)現(xiàn)的方式都實(shí)現(xiàn)的差不多了,具體的學(xué)習(xí)過程是參考ImmersionBar 學(xué)習(xí)的,受教很多,
由于設(shè)計(jì)到的代碼比較多,所以還是分享一個github 的地址吧,這樣方便大家使用
https://github.com/tsm1991/TsmBottomSheetDialog