沉浸式系列總結

前言

關於適配一直是Android開發者頭疼的問題,恰好這段時間遇到了這個問題,決定仔細的學習一下(瘋狂百度學習)。android

關於沉浸式

簡介

關於沉浸式,其實在安卓裏面並非沉浸式,只是對狀態欄和虛擬導航欄的透明化操做而已,可是市面上你們都這麼叫,因此就成爲了沉浸式導航欄了。(本文測試皆爲GoogleAPI的版本模擬器,所有親身測試過)bash

看了許多的博客和代碼,發現這個東西還真是夠複雜的,由於安卓太多的廠商有太多的ROM,以致於適配不過來。可是總的來講,沉浸式分爲四個部分:app

  • Android 4.4 (API 19) - Android 5.0 (API 21):這個階段能夠實現沉浸式,可是效果不是很好,主要的實現方式爲:設置FLAG_TRANSLUCENT_STATUS將狀態欄設置爲透明而且設置爲全屏,而後添加一個和StatusBar 同樣大小的View,將View 的 background 設置爲咱們想要的顏色,從而來實現沉浸式。
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
複製代碼
  • Android 5.0 (API 21)以上版本:在這個階段,系統加入了一個重要的屬性和方法:android:statusBarColor(對應的方法爲setStatusBarColor),經過這個方法咱們就能夠實現沉浸式了。工具

  • Android 6.0 (API 23) 以上版本:爲啥要加這個階段咧,由於這個階段開始,咱們能夠改狀態欄的繪製模式,能夠顯示白色或淺黑色的內容和圖標。(深色或者淺色)佈局

關於狀態欄的操做

Android 4.4 - Android 5.0 (API 19 -21)

這個階段沒有真正意義上的沉浸式,咱們只是將狀態欄透明化了而已。學習

Activity時

沒有設置沉浸式時: 測試

咱們設置狀態欄爲透明,而且設置一個Status大小的view:ui

getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
複製代碼

這裏咱們須要解釋一下:spa

當window的這個屬性有效的時候,會自動設置 system ui visibility的標誌
SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
複製代碼

咱們來看下效果:3d

能夠看到,狀態欄已經透明化了,而且全屏顯示了,可是這樣子啊,咱們的組件就直接顯示到最上面去了。咱們還須要設置一個和狀態欄同樣大小的View去填充,那麼,咱們能夠這麼實現

  • 在佈局文件中填充一個View比做狀態欄

  • 設置根佈局的paddingTop 屬性來填充狀態欄

Fragment時

當界面爲底部導航欄擁有多個Fragment時,咱們來看看適配的效果

沒有適配時

我門使用上面的Activity的適配方法適配後。

能夠看到,適配時成功的,可是,咱們須要把頂部填充一下。由於這個適配時針對頂部透明的。

側邊欄時

沒有適配時

使用如下方法適配時

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

//將側邊欄頂部延伸至status bar
drawer.setFitsSystemWindows(true);
//將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
drawer.setClipToPadding(false);
複製代碼

實現的效果

完美實現

封裝

那麼,針對安卓4.4到5.0的適配方法,咱們能夠封裝爲以下:

public class SimpleStatusBarAdapter {
​
 /*
 適配安卓4.4--5.0  測試手機  谷歌安卓模擬器
 */
 public static void setAdapter(Activity activity) {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 }
 }
​
 /*
 適配安卓4.4--5.0  測試手機  谷歌安卓模擬器  將跟佈局的paddingTop設置爲狀態欄的高度
 */
 public static void setAdapter(Fragment fragment, View rootView, boolean headImage) {
​
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 if (!headImage) {
 int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
 rootView.setPadding(0, height, 0, 0);
 }
 }
 }
​
 /*
 適配安卓4.4--5.0  測試手機  谷歌安卓模擬器  側邊欄爲DrawerLayout時,設置這個屬性實現沉浸式
 */
 public static void setAdapter(DrawerLayout drawer) {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 //將側邊欄頂部延伸至status bar
 drawer.setFitsSystemWindows(true);
 //將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
 drawer.setClipToPadding(false);
 }
 }
}
複製代碼

Android 5.0 (API 21)以上

從Android 5.0開始,安卓系統加入了一個比較重要的方法setStatusBarColor (對應屬性:android:statusBarColor),經過這個方法,能夠很輕鬆地實現沉浸式狀態欄。

Activity時

未適配時

使用Android4.4時的適配方法的界面

能夠看到,設置以後只是透明化了一點。可是仍是沒有到理想的狀態。

咱們開始使用API21以後的方法來實驗一下:

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
複製代碼

這裏須要解釋一下的是:

想要這個方法生效,必須還要配合一個Flag一塊兒使用,必須設置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,
而且不能設置FLAG_TRANSLUCENT_STATUS(Android 4.4才用這個)。
複製代碼

再來運行一下看下效果:

完美實現

也能夠在Theme中使用

<style name="MDTheme" parent="Theme.AppCompat.Light.NoActionBar">
 <item name="android:windowTranslucentStatus">false</item>
 <item name="android:windowDrawsSystemBarBackgrounds">true</item>
 <item name="android:statusBarColor">@color/status_color</item>
 </style>
複製代碼

若是想將狀態欄直接所有隱藏掉咱們能夠這麼設置

<style name="ImageTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
 <item name="android:windowTranslucentNavigation">true</item>
 <item name="android:windowTranslucentStatus">true</item>
 <!-- 設置statusBarColor 爲透明-->
 <item name="android:statusBarColor">@android:color/transparent</item>
 </style>

還有

 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
複製代碼

咱們看看效果:

這裏,咱們看到,底部的虛擬導航欄也被透明化了。

麼事,咱們有百度,咱們去修改

<item name="android:windowTranslucentNavigation">false</item>
複製代碼

運行,,,結果和咱們想象的徹底不一樣,狀態欄也失效了。

可是,我找到了解決方法

activity.getWindow().getDecorView().
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
複製代碼

添加這個便可不透明化底部虛擬導航欄

Fragment時

咱們先來看看,徹底沒有適配的時候

能夠看到,狀態欄徹底擋住了咱們的視圖,咱們來設置上面的方式試試

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 //注意要清除 FLAG_TRANSLUCENT_STATUS flag
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
複製代碼

而後咱們會看到,emmm

咱們把狀態欄的顏色寫死了,那麼要適配動態的Fragment,咱們只能將狀態欄直接所有隱藏掉,直接這麼寫

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 //注意要清除 FLAG_TRANSLUCENT_STATUS flag
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
 activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
複製代碼

而後,運行看看

能夠看到,確實是實現了。可是,這裏須要咱們適配頂部的高度

if (!headImage) {
 int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
 rootView.setPadding(0, height, 0, 0);
 }
複製代碼

側邊欄時

咱們只須要實現一下方法便可完美實現沉浸式

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(activity.getResources().getColor(R.color.status_translution));
 activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

toolbar.setFitsSystemWindows(true);
複製代碼

如圖:

今天發現上面的適配方法有個問題,遇到水滴屏就會變形。而後,我又試出了新的適配方法。

首先,在NavigationView中加入

app:insetForeground="@null"
複製代碼

其餘的照上面適配 而後加上

int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = context.getResources().getDimensionPixelSize(resourceId);
 View headerView = navigationView.getHeaderView(0);
 navigationView.setForeground(new BitmapDrawable());
 coordinatorLayout.setPadding(0, height, 0, 0);
 headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
 coordinatorLayout.setBackground(toolbar.getBackground());
複製代碼

這樣子,就完美適配了,嘗試過水滴屏(小米),劉海屏(小米)都沒有啥問題

Android 6.0 (API 23) 以上

在這個階段,其實和5.0時差很少,只不過多了對狀態欄字色和圖標的顏色的操做API,由於以前的沉浸式不能設置狀態欄爲淺色調,否則就會看的很彆扭。

Activity時

咱們來看看淺色時的界面

是否是以爲很彆扭,這種狀況在6.0以前是沒法解決的,在這以後,咱們就能夠完美解決了。

咱們只須要在5.0的基礎上加上

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 activity.getWindow().getDecorView().setSystemUiVisibility(
 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
複製代碼

看看效果

能夠看到,完美實現。

另外,還能夠在style中使用

<!-- Android 6.0以上 狀態欄字色和圖標爲淺黑色-->
<item name="android:windowLightStatusBar">true</item>
複製代碼

6.0版本之上跟5.0之上同樣的,因此就不用區分各類狀況了。

關於setSystemUiVisibility方法中參數的意義

示例

咱們常常看到

View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
 | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
複製代碼

那麼其中的View.SYSTEM_UI_FLAG_HIDE_NAVIGATION都是些啥咧?每次都是網上查一下,而後直接粘貼複製使用,也沒多想,今天就來總結一下。

常見參數的意義

參數 意義
SYSTEM_UI_FLAG_FULLSCREEN 隱藏狀態欄
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 半透明狀態欄
SYSTEM_UI_FLAG_HIDE_NAVIGATION 隱藏虛擬導航欄
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 半透明虛擬導航欄,會自動設置半透明狀態欄
SYSTEM_UI_FLAG_IMMERSIVE 自動隱藏狀態欄和虛擬導航欄
SYSTEM_UI_FLAG_IMMERSIVE_STIKY 自動隱藏狀態欄和虛擬導航欄

關於setFlag方法中參數的意義

示例

window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
複製代碼

常見參數的意義

類型 參數 意義
int FLAG_BLUR_BEHIND 讓該window後全部東西都模糊(blur)
int FLAG_DIM_BEHIND 讓該window後全部的東西都成暗淡(dim)
int FLAG_DITHER 開啓抖動(dithering)
int FLAG_FORCE_NOT_FULLSCREEN 恢復window非全屏顯示
int FLAG_FULLSCREEN 讓window進行全屏顯示
int FLAG_KEEP_SCREEN_ON 當該window對用戶可見時,讓設備屏幕處於高亮(bright)狀態。
int FLAG_LAYOUT_IN_SCREEN 讓window佔滿整個手機屏幕,不留任何邊界(border)
int FLAG_LAYOUT_NO_LIMITS window大小再也不不受手機屏幕大小限制,即window可能超出屏幕以外,這時部份內容在屏幕以外。
int FLAG_SECURE 當該window在進行顯示的時候,不容許截屏。

關於冷啓動時,遇到水滴(劉海屏)黑色狀態欄的問題的解決方法

咱們只須要將啓動時的背景設置爲透明,而後其餘的正常適配便可,這樣子的作法是適配瞞天過海的啓動頁

<item name="android:windowIsTranslucent">true</item>
複製代碼

最終封裝

咱們從前到後,測試了許多效果,那麼可不能夠封裝爲一個工具類,針對性的適配,咱們來嘗試下。

第一步:適配單個Activity

public static void setActivityAdapter(Activity activity,boolean isSetLightStatusBar) {
 /*
 適配安卓4.4--5.0
 */
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 }
 /*
 適配安卓5.0以上
 */
 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//                //注意要清除 FLAG_TRANSLUCENT_STATUS flag
 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
 activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
​
 /*
 適配6.0以上狀態欄文字顏色,設置爲深色
 */
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 if (isSetLightStatusBar)
 activity.getWindow().getDecorView().setSystemUiVisibility(
 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
 }
​
 }
 }
複製代碼

第二步:適配Fragment

public static void setFragmentAdapter(Fragment fragment, View rootView, boolean headerIsImage) {
 /*
 適配安卓4.4--5.0
 */
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 if (!headerIsImage) {
 int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
 rootView.setPadding(0, height, 0, 0);
 }
 }
 /*
 適配安卓5.0以上
 */
 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
 if (!headerIsImage) {
 int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
 rootView.setPadding(0, height, 0, 0);
 }
 }
 }
複製代碼

第三步:適配側邊欄

/*
 toolbar.getBackground()
 */
 public static void setNavigationViewAdapter(DrawerLayout drawer, Context context, NavigationView navigationView, View rootLayout, Drawable bg) {
 /*
 適配安卓4.4--5.0
 */
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 //將側邊欄頂部延伸至status bar
 drawer.setFitsSystemWindows(true);
 //將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
 drawer.setClipToPadding(false);
 }
 /*
 適配安卓5.0以上
 */
 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
 int height = context.getResources().getDimensionPixelSize(resourceId);
 View headerView = navigationView.getHeaderView(0);
 navigationView.setForeground(new BitmapDrawable());
 rootLayout.setPadding(0, height, 0, 0);
 headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
 rootLayout.setBackground(bg);
 }
 }
複製代碼

最終,我總結的是,咱們可使用工具類,可是,最終仍是須要本身應對部分適配不成功的問題,因此仍是須要本身去理一理,輪子雖好,可不要貪杯哦。

好了,這篇文章先到這裏,後期有新的的再補充(客套話)。

相關文章
相關標籤/搜索