Android 沉浸式解析和輪子使用

前言

我們先一起來回顧一下實現沉浸式狀態欄的一般套路。在 Android 上,關於對 StatusBar(狀態欄)的操作,一直都在不斷改善,並且表現越來越好,在 Android4.4 以下,我們可以對 StatusBar 和 NavigationBar 進行顯示和隱藏操作。但是直到 Android4.4,我們才能真正意義上的實現沉浸式狀態欄。從 Android4.4 到現在(Android 9),關於沉浸式大概可以分成三個階段:

  • Android4.4(API 19)- Android 5.0(API 21):這個階段可以實現沉浸式,但是表現得還不是很好,實現方式爲: 通過 FLAGTRANSLUCENTSTATUS 設置狀態欄爲透明並且爲全屏模式,然後通過添加一個與 StatusBar 一樣大小的 View,將View 的 background 設置爲我們想要的顏色,從而來實現沉浸式。
  • Android 5.0(API 21)以上版本:在Android 5.0 的時候,加入了一個重要的屬性和方法 android:statusBarColor (對應方法爲 setStatusBarColor),通過這個方法我們就可以輕鬆實現沉浸式。也就是說,從 Android5.0 開始,系統才真正的支持沉浸式。
  • Android 6.0(API 23)以上版本:其實 Android6.0 以上的實現方式和 Android 5.0+是一樣,爲什麼要將它歸爲一個單獨重要的階段呢?是因爲從 Android 6.0(API 23)開始,我們可以改狀態欄的繪製模式,可以顯示白色或淺黑色的內容和圖標(除了魅族手機,魅族自家有做源碼更改,6.0 以下就能實現)。

總結:這三個階段的 Android 上 API 版本混亂,各種 Flag 林立。再加上各大廠商的定製化可謂是火上澆油,讓安卓開發者異常頭疼。

一、沉浸式三個階段使用

我們將從沉浸式支持的三個階段和支持的功能出發,去了解出現的相關背景,然後去了解怎麼實現三個階段的沉浸式。

1.1 Android4.0-Android5.0設置沉浸體驗

在 android4.4 及以上版本中爲 setSystemUiVisibility() 方法引入了一個新的 flag:SYSTEMUIFLAGIMMERSIVE,它可以使你的 app 實現真正意義上的全屏體驗。當 SYSTEMUIFLAGIMMERSIVE、SYSTEMUIFLAGHIDENAVIGATION 和SYSTEMUIFLAG_FULLSCREEN 三個 flag 一起使用的時候,可以隱藏狀態欄與導航欄,同時讓你的app可以捕捉到用戶的所有觸摸屏事件。從 Android4.4 以上版本纔是真正的可以設置沉浸式體驗,但也僅僅是操作狀態欄和導航欄的顯示與隱藏

1.1.1 FLAGTRANSLUCENTSTATUS

當沉浸式全屏模式啓用的時候,你的 activity 會繼續接受各類的觸摸事件。用戶可以通過在狀態欄與導航欄原來區域的邊緣向內滑動讓系統欄重新顯示。這個操作清空了 SYSTEM_UI_FLAG_HIDE_NAVIGATIONSYSTEM_UI_FLAG_FULLSCREEN,如果沒有兩個標誌的話,系統欄重新變得可見。如果設置了兩個標籤的話,這個操作同時也觸發了 View.OnSystemUiVisibilityChangeListener。然而, 如果你想讓系統欄在一段時間後自動隱藏的話,你應該使用 SYSTEMUI_FLAG_IMMERSIVE_STICKY 標籤

展示了各種不同的“沉浸式”狀態:

在上圖中:

  • 非沉浸模式 —— 展示了應用進入沉浸模式之前的狀態。也展示了設置 IMMERSIVE 標籤後用戶滑動展示系統欄的狀態。用戶滑動後,SYSTEM_UI_FLAG_HIDE_NAVIGATIONSYSTEM_UI_FLAG_FULLSCREEN 就會被清除,系統欄就會重新顯示並保持可見。請注意,最好的方式就是讓所有的 UI 控件與系統欄的顯示隱藏保持同步,這樣可以減少屏幕顯示所處的狀態,同時提供了更無縫平滑的用戶體驗。因此所有的UI控件跟隨系統欄一同顯示。一旦應用進入了沉浸模式,UI 控件也跟隨着系統欄一同隱藏。爲了確保 UI 的可見性與系統欄保持一致,我們需要一個監聽器 View.OnSystemUiVisibilityChangeListener 來監聽系統欄的變化。這在下一節中將詳細講解。
  • 提示氣泡——第一次進入沉浸模式時,系統將會顯示一個提示氣泡,提示用戶如何再讓系統欄顯示出來。請注意,如果爲了測試你想強制顯示提示氣泡,你可以先將應用設爲沉浸模式,然後按下電源鍵進入鎖屏模式,並在 5 秒中之後打開屏幕。
  • 沉浸模式—— 這張圖展示了隱藏了系統欄和其他UI控件的狀態。你可以設置 IMMERSIVE 和 IMMERSIVE_STICKY 來進入這個狀態。
  • 粘性標籤——這就是你設置了 IMMERSIVE_STICKY 標籤時的 UI 狀態,用戶會向內滑動以展示系統欄。半透明的系統欄會臨時的進行顯示,一段時間後自動隱藏。滑動的操作並不會清空任何標籤,也不會觸發系統 UI 可見性的監聽器,因爲暫時顯示的導航欄並不被認爲是一種可見的狀態。

注意,immersive 類的標籤只有在與 SYSTEMUIFLAGHIDENAVIGATION、SYSTEMUIFLAG_FULLSCREEN 中一個或兩個一起使用的時候纔會生效。你可以只使用其中的一個,但是一般情況下你需要同時隱藏狀態欄和導航欄以達到沉浸的效果。

1.1.2 兩種使用這個屬性的方式:

  • 在使用時候,我們通常需要考慮的是狀態欄一值顯示在頂部而不會隱藏或者被遮擋(其他 app 情況如:讀書 app 或者是遊戲 app 則需要隱藏頂部狀態欄),所以只需要通過代碼設置 FLAGTRANSLUCENTSTATU
 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  • 通過主題theme設置屬性windowTranslucentStatus:
    <style name="Theme" parent="Theme.Design.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
    </style>

效果如下:

效果如上圖,可以看出,沉浸式的效果是出來了,但是也有一個問題,我們的標題欄和狀態欄重疊了,相當於整個佈局上移了StatusBar 的高度。

爲了讓標題欄回到原來的位置並且適應標題欄的顏色,我們在標題欄的上方添加一個大小和 StatusBar 大小一樣假的狀態欄 View,View 的 BackgroundColor 可以自己設置成標題欄一樣的顏色也可以是其他顏色,這個 View 起到一個佔位的作用。這個時候,標題欄就會下移 StatusBar 的高度,回到正常的位置。

通過設置 paddingTop 重新繪製標題欄高度代碼如下:

 View statusBarView = mDecorView.findViewById(IMMERSION_STATUS_BAR_VIEW);
        if (statusBarView == null) {
            statusBarView = new View(mActivity);
            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                    mBarConfig.getStatusBarHeight());
            params.gravity = Gravity.TOP;
            statusBarView.setLayoutParams(params);
            statusBarView.setVisibility(View.VISIBLE);
            statusBarView.setId(IMMERSION_STATUS_BAR_VIEW);
            mDecorView.addView(statusBarView);
        }
        if (mBarParams.statusBarColorEnabled) {
            statusBarView.setBackgroundColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                    mBarParams.statusBarColorTransform, mBarParams.statusBarAlpha));
        } else {
            statusBarView.setBackgroundColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                    Color.TRANSPARENT, mBarParams.statusBarAlpha));
        }

添加上述代碼後,效果如下:

通過以上就可以實現 Android 4.4 上的沉浸式狀態欄。

小結:Android4.4-Android5.0的步驟就是爲window添加 FLAGTRANSLUCENTSTATUS 的 Flag,然後添加一個假的狀態欄,通過上述方法設置的沉浸式在 Android4.4-Android5.0 之間的效果如貼圖,狀態欄頂部是有一個黑色陰影漸變,在5.0版本版本以上被修復了。如果是一張圖片沉浸到狀態欄則不需要設置這個假的狀態欄,只需要設置,FLAGTRANSLUCENTSTATUS 就 OK。並且在 Android4.4-Android5.0 是沒有提供改變狀態顏色的屬性,所以只能通過新增加一個假的狀態欄方式改變背景顏色。

1.2 Android5.0 以上設置狀態欄背景顏色

Android 5.0 是一個里程碑式的版本,從Android 5.0 開始,Google 推出了全新的設計規範 Material Design,並且原生控件就可以實現一些炫酷的 UI 動效。從這個版本開始,google 加入了一個比較重要的方法 setStatusBarColor (對應屬性:android:statusBarColor),通過這個方法,可以很輕鬆地實現沉浸式狀態欄。方法如下:

public abstract void setStatusBarColor(@ColorInt int color);

注意看這個方法的註釋,想要這個方法生效,必須還要配合一個Flag一起使用,必須設置 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,並且不能設置 FLAG_TRANSLUCENT_STATUS(Android 4.4 才用這個),所以和 4.4 互斥不能共用。所以Android5.0以上可以設置狀態欄和導航欄背景顏色,但還不能改變狀態欄和導航欄圖標和字色。

1.2.1 FLAGDRAWSSYSTEMBARBACKGROUNDS 屬性

解釋:設置了 FLAGDRAWSSYSTEMBARBACKGROUNDS,表明會 Window 負責系統bar的 background 繪製,繪製透明背景的系統 bar(狀態欄和導航欄),然後用 getStatusBarColor() 和 getNavigationBarColor() 的顏色填充相應的區域。這就是 Android 5.0 以上實現沉浸式導航欄的原理。

實現沉浸式添加如下代碼:

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    //注意要清除 FLAG_TRANSLUCENT_STATUS flag
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));

效果如下:

如果在開發的時候是通過設置主題的方式設置,則需要在values-v21文件夾下添加如下主題,達到兼容目的

<style name="Theme" parent="Theme.Design.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/holo_red_light</item>
    </style>

1.2.2 圖片延伸到狀態欄

在 Android 5.0 使圖片延伸到狀態欄,只需設置 windowTranslucentStatus,將 statusBarColor 設置爲透明即可。主題方式設置如下:

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

在開發過程中,使用代碼設置 windowTranslucentStatus 需要通過版本號的判斷兼容 Android5.0 以下和 Android 5.0 以上。

1.3 Android 6.0 +實現狀態欄字色和圖標淺黑色

使用 Android6.0 以下版本沉浸式的時候會遇到一個問題,那就是 Android 系統狀態欄的字色和圖標顏色爲白色,當狀態欄顏色接近淺色的時候,狀態欄上的內容就看不清了。Android 6.0 新添加了一個屬性來解決這個問題,屬性是 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,可以設置狀態欄字色和圖標淺黑色。

1.3.1 SYSTEMUIFLAGLIGHTSTATUS_BAR

解釋:爲 setSystemUiVisibility(int) 方法添加的 Flag,請求 status bar 繪製模式,它可以兼容亮色背景的status bar 。要在設置了 FLAGDRAWSSYSTEMBARBACKGROUNDSflag ,同時清除了 FLAGTRANSLUCENTSTATUSflag 纔會生效。

通過代碼設置:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

效果如下:

並且還可以在主題中使用屬性,並且該主題需要放在 values-v23 文件夾下相應 Android6.0 以上才能生效:

<style name="Theme" parent="Theme.Design.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/holo_red_light</item>
        <!-- Android 6.0以上 狀態欄字色和圖標爲淺黑色-->
        <item name="android:windowLightStatusBar">true</item>
    </style>

二、ZanImmersionBar 輪子的原理和使用

在實際開發過程中,我們不僅僅只會遇到以上三種版本兼容問題,還需要考慮如:不同手機品牌,動態該狀態欄背景,以及 Fragment 中需要有自己的狀態欄顏色場景。所以需要綜合考慮多種場景,達到能適配多種開發情況的要求。

總結出以下場景:

綜合以上場景並且參考github例子進行封裝之後得到了 ZanImmersionBar 這個輪子

2.1 原理:

我們希望將設置沉浸式效果都封裝在一個類裏面,想達到所有的效果通過一個方法設置,但很多的效果中都會有重複的設置步驟,並且每個效果的方法太多則分不清使用哪個,所以將設置沉浸式效果拆分成幾個步驟,而想要設置個性效果,通過方法設置參數,最後通過init方法收集所有參數讓後統一設置參數屬性。

ZanImmersionBar.with(this).init();//該方法將進行以下步驟處理沉浸式
public void init() {
        //更新Bar的參數
        updateBarParams();
        //設置沉浸式
        setBar();
        //適配狀態欄與佈局重疊問題
        fitsLayoutOverlap();
        //適配軟鍵盤與底部輸入框衝突問題
        fitsKeyboard();
        //變色view
        transformView();
    }

沉浸式設置流程如下圖:

這幾個步驟其中,獲取參數和設置沉浸式是必須經過,下面三種設置是在開發中可能遇到的情況,也是設置參數,如果匹配到了則會進行三種設置的處理,接下來主要分析下第一個步驟和第二步驟。

2.1.1 收集 bar 參數

我們使用一個對象用於存儲用戶設置的 bar 參數,這些參數有狀態欄和導航欄顏色、透明度、顯示隱藏等等,通過該對象中的參數來分別設置

public class BarParams implements Cloneable {
    /**
     * 狀態欄顏色
     */
    @ColorInt
    int statusBarColor = Color.TRANSPARENT;
    /**
     * 導航欄顏色
     */
    @ColorInt
    int navigationBarColor = Color.BLACK;
    /**
     * The Default navigation bar color.
     */
    int defaultNavigationBarColor = Color.BLACK;
    /**
     * 狀態欄透明度
     */
    @FloatRange(from = 0f, to = 1f)
    float statusBarAlpha = 0.0f;
    /**
     * 導航欄透明度
     */
    @FloatRange(from = 0f, to = 1f)
    float navigationBarAlpha = 0.0f;
      //等其他屬性
    ...
}

而這些屬性可以通過以下ZanImmersionBar提供的方法進行個性化設置,而這些方法只是將需要設置的參數添加到BarParams對象中,最後必須調用init將參數設置上去。

 ZanImmersionBar.with(this)
             .transparentStatusBar()  //透明狀態欄,不寫默認透明色
             .transparentNavigationBar()  //透明導航欄,不寫默認黑色(設置此方法,fullScreen()方法自動爲true)
             .transparentBar()             //透明狀態欄和導航欄,不寫默認狀態欄爲透明色,導航欄爲黑色(設置此方法,fullScreen()方法自動爲true)
             .statusBarColor(R.color.colorPrimary)     //狀態欄顏色,不寫默認透明色
             .navigationBarColor(R.color.colorPrimary) //導航欄顏色,不寫默認黑色
             .barColor(R.color.colorPrimary)  //同時自定義狀態欄和導航欄顏色,不寫默認狀態欄爲透明色,導航欄爲黑色
             .statusBarAlpha(0.3f)  //狀態欄透明度,不寫默認0.0f
             .navigationBarAlpha(0.4f)  //導航欄透明度,不寫默認0.0F
             .barAlpha(0.3f)  //狀態欄和導航欄透明度,不寫默認0.0f
             .statusBarDarkFont(true)   //狀態欄字體是深色,不寫默認爲亮色
             .navigationBarDarkIcon(true) //導航欄圖標是深色,不寫默認爲亮色
                           //等一些其他方法
             .init();  //必須調用方可沉浸式

設置完參數後,則需要收集這些參數,如果在Fragment中使用,則需要Activity同步Fragment的BarParams參數

//獲得Bar相關信息
//如果在Fragment中使用,讓Activity同步Fragment的BarParams參數
if (mIsFragment) {
    ZanImmersionBar immersionBar = mImmersionBarMap.get(mActivity.toString());
    if (immersionBar != null) {
        immersionBar.mBarParams = mBarParams;
    }
}

mImmersionBarMap 是個 Map,用於存儲每個 Activity 對應的 ZanImmersionBar 對象

  • 原理:其實我們通過沉浸式三代發展史,我們可以知道設置沉浸式都是通過改變Activity的window屬性達到沉浸式,所以我們想要在Fragment的定製化沉浸式,其實是通過改變Activity的BarParams參數再設置Activity的沉浸式,所以需要注意對Fragment設置沉浸式前必須保證加載Fragment的Activity先進行了ZanImmersionBar的init初始化。
  • 另例:需要注意dialogFragment和Dialog是有自己的window,該window是屬於所屬Activity的子window,所以設置dialogFragment和Dialog的沉浸式改變的是自己window裏的屬性,而不是改變Actvity的window裏的屬性。
  • 關閉銷燬:在activity的onDestroy方法中執行,因爲mImmersionBarMap存儲了每個Activity對應的ZanImmersionBar對象,所以當Activity關閉了需要及時釋放,不然mImmersionBarMap會存在過多無用的ZanImmersionBar對象。
  ZanImmersionBar.with(this).destroy(); //必須調用該方法,防止內存泄漏
   public void destroy() {
          //取消監聽
          cancelListener();
          //刪除當前界面對應的ImmersionBar對象
          Iterator<Map.Entry<String, ZanImmersionBar>> iterator = mImmersionBarMap.entrySet().iterator();
          while (iterator.hasNext()) {
              Map.Entry<String, ZanImmersionBar> entry = iterator.next();
              if (entry.getKey().contains(mImmersionBarName) || (entry.getKey().equals(mImmersionBarName))) {
                  iterator.remove();
              }
          }
      }

2.1.2 初始化狀態欄和導航欄

該方法則是將上一步的參數進行初始化,初始化過程則會根據沉浸式三個階段和不同廠商進行區分設置。需要注意小米手機系統有自己定製化設置的屬性,所以需要分區設置:

private void setBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !OSUtils.isEMUI3_x()) {
            //適配劉海屏
            fitsNotchScreen();
            //初始化5.0以上,包含5.0
            uiFlags = initBarAboveLOLLIPOP(uiFlags);
            //android 6.0以上設置狀態欄字體爲暗色
            uiFlags = setStatusBarDarkFont(uiFlags);
            //android 8.0以上設置導航欄圖標爲暗色
            uiFlags = setNavigationIconDark(uiFlags);
        } else {
            //初始化5.0以下,4.4以上沉浸式
            initBarBelowLOLLIPOP();
        }
             ... 
    }
    if (OSUtils.isMIUI6Later()) {
        //修改miui狀態欄字體顏色
        setMIUIBarDark(mWindow, MIUI_STATUS_BAR_DARK, mBarParams.statusBarDarkFont);
        //修改miui導航欄圖標爲黑色
        if (mBarParams.navigationBarEnable) {
            setMIUIBarDark(mWindow, MIUI_NAVIGATION_BAR_DARK, mBarParams.navigationBarDarkIcon);
        }
    }
    ...
}

我們可以重點看下5.0以上狀態欄和導航欄初始化,設置初始化window屬性讓後設置導航欄和狀態欄顏色。

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int initBarAboveLOLLIPOP(int uiFlags) {
    //Activity全屏顯示,但狀態欄不會被隱藏覆蓋,狀態欄依然可見,Activity頂端佈局部分會被狀態欄遮住。
    uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
    mWindow.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //判斷是否存在導航欄
    if (mBarConfig.hasNavigationBar()) {
        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    }
    //需要設置這個才能設置狀態欄和導航欄顏色
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    //設置狀態欄顏色
    if (mBarParams.statusBarColorEnabled) {
        mWindow.setStatusBarColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                mBarParams.statusBarColorTransform, mBarParams.statusBarAlpha));
    } else {
        mWindow.setStatusBarColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                Color.TRANSPARENT, mBarParams.statusBarAlpha));
    }
      ...
    return uiFlags;
}

通過代碼分析可以看到其實 ZanImmersionBar 所做的事情就是將設置沉浸式方法進行步驟拆分和增加個性屬性設置,並且將沉浸式三個階段和不同廠商進行區分獨立方法設置調用。

2.2 建議和使用

  • 建議在 BaseActivity 中初始化和銷燬
    public class BaseActivity extends AppCompatActivity {

         @Override
         protected void onCreate(@Nullable Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             // 所有子類都將繼承這些相同的屬性,請在設置界面之後設置
             ZanImmersionBar.with(this).init();  
         }
         @Override
         protected void onDestroy() {
             super.onDestroy();
             // 必須調用該方法,防止內存泄漏
             ZanImmersionBar.with(this).destroy();  
         }
     }
  • 展示 Activity 中使用效果

2.3 在 Fragment 使用 ZanImmersionBar

注意在 Fragment 中使用 ZanImmersionBar 需要在承載的 Activity 中初始化 ZanImmersionBar,不然會拋出異常。

    public abstract class BaseFragment{
         @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
             super.onViewCreated(view, savedInstanceState);
             initImmersionBar();
    }
         @Override
       public void initImmersionBar() {
            ZanImmersionBar.with(this).keyboardEnable(true).init();
        }
    }

具體的Fragment調用以下方法:

    @Override
    public void initImmersionBar() {
        super.initImmersionBar();
        ZanImmersionBar.with(this)
                .statusBarDarkFont(true)
                .statusBarColor(R.color.btn1)
                .navigationBarColor(R.color.btn1)
                .init();
    }
  • 設置 Fragment 的狀態欄顏色和狀態欄字體顏色效果

2.4 在 Dialog 中實現沉浸式

在 Dialog 中設置 ZanImmersionBar 方式和在 Fragment 或者 Activity 一樣,如果在 Fragment 或者 Activity 中有設置並且 dialog 出現不需要改變狀態欄則不用設置 ZanImmersionBar,如果需要做定製化上面的高級用法在 Dialog 也支持

  • 結合 dialogFragment 中使用
      ZanImmersionBar.with(this).init();
  • 其他dialog
      ZanImmersionBar.with(this, dialog).init();

2.5 在 PopupWindow 中實現沉浸式

重點是調用以下方法,但是此方法會導致有導航欄的手機底部佈局會被導航欄覆蓋,還有底部輸入框無法根據軟鍵盤彈出而彈出。這個屬性在頂部彈出的時候是需要使用,如果是底部彈框需要看情況而定。

    popupWindow.setClippingEnabled(false);

2.6 狀態欄與佈局頂部重疊解決方案,六種方案任選其一(可選)

正常使用 ZanImmersionBar 一般不需要考慮重疊問題但在項目中接入 ZanImmersionBar 並且頁面沒有考慮給頭部控件預留出狀態欄的高度,並且需要將頁面內容沉浸到狀態欄或者做定製化狀態欄,這種情況下需要考慮重疊問題。之前說到 Android4.4 版本的時候解決重疊的方式是一種,也可以參考一下幾種方式解決狀態欄與佈局頂部重疊問題。

① 使用 dimen 自定義狀態欄高度,不建議使用,因爲設備狀態欄高度並不是固定的

在 values-v19/dimens.xml 文件下

        <dimen name="status_bar_height">25dp</dimen>

在values/dimens.xml文件下

        <dimen name="status_bar_height">0dp</dimen>

然後在佈局界面添加view標籤,高度指定爲statusbarheight

       <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:app="http://schemas.android.com/apk/res-auto"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:background="@color/darker_gray"
           android:orientation="vertical">

           <View
               android:layout_width="match_parent"
               android:layout_height="@dimen/status_bar_height"
               android:background="@color/colorPrimary" />

           <android.support.v7.widget.Toolbar
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:background="@color/colorPrimary"
               app:title="方法一"
               app:titleTextColor="@android:color/white" />
       </LinearLayout>

② 使用系統的fitsSystemWindows屬性,使用該屬性不會導致輸入框與軟鍵盤衝突問題,不要再Fragment使用該屬性

       <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:orientation="vertical"
           android:fitsSystemWindows="true">
       </LinearLayout>

然後使用ImmersionBar時候必須指定狀態欄顏色

       ZanImmersionBar.with(this)
            .statusBarColor(R.color.colorPrimary)
            .init();

注意:ZanImmersionBar一定要在設置完佈局以後使用

③ 使用ZanImmersionBar的fitsSystemWindows(boolean fits)方法

        ZanImmersionBar.with(this)
            .fitsSystemWindows(true)  //使用該屬性,必須指定狀態欄顏色
            .statusBarColor(R.color.colorPrimary)
            .init();

④ 使用ZanImmersionBar的statusBarView(View view)方法

在標題欄的上方增加View標籤,高度指定爲0dp

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               xmlns:app="http://schemas.android.com/apk/res-auto"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="@color/darker_gray"
               android:orientation="vertical">

               <View
                   android:layout_width="match_parent"
                   android:layout_height="0dp"
                   android:background="@color/colorPrimary" />

               <android.support.v7.widget.Toolbar
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:background="@color/colorPrimary"
                   app:title="方法四"
                   app:titleTextColor="@android:color/white" />
        </LinearLayout>

然後使用ZanImmersionBar的statusBarView方法,指定view就可以啦

         ZanImmersionBar.with(this)
               .statusBarView(view)
               .init();
         //或者
         //ZanImmersionBar.setStatusBarView(this,view);

⑤ 使用ZanImmersionBar的titleBar(View view)方法,原理是設置paddingTop

             ZanImmersionBar.with(this)
                   .titleBar(view) //可以爲任意view,如果是自定義xml實現標題欄的話,最外層節點不能爲RelativeLayout
                   .init();
             //或者
             //ZanImmersionBar.setTitleBar(this, view);

⑥ 使用ZanImmersionBar的titleBarMarginTop(View view)方法,原理是設置marginTop

             ZanImmersionBar.with(this)
                   .titleBarMarginTop(view)  //可以爲任意view
                   .statusBarColor(R.color.colorPrimary)  //指定狀態欄顏色,根據情況是否設置
                   .init();
             //或者使用靜態方法設置
             //ZanImmersionBar.setTitleBarMarginTop(this,view);

總結

在處理 Android 沉浸式狀態欄和導航欄開始會很頭大,並且會不理解相關設置的 window 的 FLAG 屬性,想要分清楚這些屬性的大意需要從出現背景出發拆封,先熟悉沉浸式出現的3個階段的屬性和版本能做什麼和不能做什麼,然後再去了解各個廠家定製化的屬性在哪些版本階段使用,以及是否需要對異形屏適配,最後纔是在實際開發和需求中對狀態欄和導航欄處理。不過在 Android6.0 以後版本廠家的定製化狀態已經越來越沒有意義了,以後 Android 原生趨勢也將會讓開發者使用沉浸式越來越舒服。

本文轉載自公衆號有贊coder(ID:youzan_coder)

原文鏈接

https://mp.weixin.qq.com/s/F9ghwjCeis7bVWIEpHybyA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章