1. 程式人生 > >android4.4以上沉浸式狀態列和導航欄實現以及Bar的其他管理

android4.4以上沉浸式狀態列和導航欄實現以及Bar的其他管理

自從android4.4開始,android手機狀態列再也不是一成黑的時代,之前叫做變色龍,miui6釋出會把他叫做沉浸式,之後大家就自然而然的接受了沉浸式這個名稱,其實實際應該叫做Translucent Bar,即為透明狀態列。
  沉浸式實現原理其實是使整個activity佈局延伸到整個螢幕,然後使狀態列變成透明色,有些手機會有導航欄,同樣也可以把導航欄變成透明色,這樣會使一些app更加美觀。

先看兩個概念

  • 狀態列

     

    狀態列.png

  • 導航欄

     

    導航欄.png

廢話不多說了,直接看GIF

[圖片上傳失敗...(image-102c4b-1534838576539)]

1、引入

github倉庫地址:https://github.com/gyf-dev/ImmersionBar

logo.png

dependencies {
    implementation 'com.gyf.immersionbar:immersionbar:2.3.1'
}

關於全面屏與劉海

  • 解決全面屏上下部分留黑或留白問題,以下三種任選其一,或者都寫

    ① 在manifest的Application節點中加入

      <meta-data 
        android:name="android.max_aspect"
        android:value="2.1" />
    

    ② 在manifest的Application節點下加入如下屬性,這句話的意思是支援分屏模式

       android:resizeableActivity="true"
    

    ③ 升級targetSdkVersion為25以上版本

2、特性

1)基本介紹

  • 基礎用法,建議在BaseActivity裡呼叫
public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ImmersionBar.with(this).init(); //初始化,預設透明狀態列和黑色導航欄
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ImmersionBar.with(this).destroy(); //不呼叫該方法,如果介面bar發生改變,在不關閉app的情況下,退出此介面再進入將記憶最後一次bar改變的狀態
    }
}
  • 高階用法,如果基礎用法不能滿足你的需求,可以試試這裡的方法
        ImmersionBar.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)   //狀態列字型是深色,不寫預設為亮色
                 .flymeOSStatusBarFontColor(R.color.btn3)  //修改flyme OS狀態列字型顏色
                 .fullScreen(true)      //有導航欄的情況下,activity全屏顯示,也就是activity最下面被導航欄覆蓋,不寫預設非全屏
                 .hideBar(BarHide.FLAG_HIDE_BAR)  //隱藏狀態列或導航欄或兩者,不寫預設不隱藏
                 .addViewSupportTransformColor(toolbar)  //設定支援view變色,可以新增多個view,不指定顏色,預設和狀態列同色,還有兩個過載方法
                 .titleBar(view)    //解決狀態列和佈局重疊問題,任選其一
                 .statusBarView(view)  //解決狀態列和佈局重疊問題,任選其一
                 .fitsSystemWindows(true)    //解決狀態列和佈局重疊問題,任選其一,預設為false,當為true時一定要指定statusBarColor(),不然狀態列為透明色
                 .supportActionBar(true) //支援ActionBar使用
                 .statusBarColorTransform(R.color.orange)  //狀態列變色後的顏色
                 .navigationBarColorTransform(R.color.orange) //導航欄變色後的顏色
                 .barColorTransform(R.color.orange)  //狀態列和導航欄變色後的顏色
                 .removeSupportView(toolbar)  //移除指定view支援
                 .removeSupportAllView() //移除全部view支援
                 .addTag("tag")  //給以上設定的引數打標記
                 .getTag("tag")  //根據tag獲得沉浸式引數
                 .reset()  //重置所以沉浸式引數
                 .keyboardEnable(true)  //解決軟鍵盤與底部輸入框衝突問題,預設為false
                 .setOnKeyboardListener(new OnKeyboardListener() {    //軟鍵盤監聽回撥
                   @Override
                   public void onKeyboardChange(boolean isPopup, int keyboardHeight) {
                       LogUtils.e(isPopup);  //isPopup為true,軟鍵盤彈出,為false,軟鍵盤關閉
                   }
              })
                 .keyboardMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)  //單獨指定軟鍵盤模式
                 .init();  //必須呼叫方可沉浸式

2)詳細介紹

  • 解決狀態列和佈局頂部重合

上面已經說了,沉浸式原理就是使整個佈局延伸到狀態列和導航欄,既然這樣必然導致一個問題,就是狀態列和佈局頂部重疊,直接看圖

狀態列和佈局頂部重疊.png

 

眼神好的同學已經看到上圖中給了五種解決方案啦,在這裡說一下

  • 1️⃣ 使用dimen自定義狀態列高度

    • 在values-v19/dimens.xml檔案下
        <dimen name="status_bar_height">25dp</dimen>
    
    • 在values/dimens.xml檔案下
        <dimen name="status_bar_height">0dp</dimen>
    
    • 然後在佈局介面新增view標籤,高度指定為status_bar_height
       <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>
    
  • 2️⃣ 使用系統的fitsSystemWindows屬性

    • 在佈局檔案的根節點使用android:fitsSystemWindows="true"屬性
        <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時候必須指定狀態列顏色
        ImmersionBar.with(this)
             .statusBarColor(R.color.colorPrimary)
             .init();
    
  • 3️⃣ 使用ImmersionBar的fitsSystemWindows(boolean fits)方法
  • 實現原理是獲得rootView的根節點,然後設定距離頂部的padding值為狀態列的高度值
```java
    ImmersionBar.with(this)
        .statusBarColor(R.color.colorPrimary)
        .fitsSystemWindows(true)  //使用該屬性必須指定狀態列的顏色,不然狀態列透明,很難看
        .init();
```
  • 4️⃣ 使用ImmersionBar的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>
    
    • 然後使用ImmersionBar的statusBarView方法,指定view就可以啦,實現原理:ImmersionBar獲取狀態列的高度,傳入view,設定高度為獲取到的狀態列高度
         ImmersionBar.with(this)
               .statusBarView(view)
               .init();
    
  • 5️⃣ 使用ImmersionBar的titleBar(View view)方法

        ImmersionBar.with(this)
                  .titleBar(view) //指定標題欄view
                  .init();
  • 總結:這五種方法,任選其一使用就可以了,不要一起使用哦,根據專案而定,比如有側邊欄的,建議使用第1️⃣種或者第4️⃣種或者第5️⃣種,最後來一張效果圖

     

    五種方法最後效果圖.jpg

  • 在Fragment中實現沉浸式

注意:2.2.6版本已將ImmersionFragment這個類標記為過時,請使用者自行使用懶載入方式實現

  • 在Fragment使用ImmersionBar

    第一種,當結合viewpager使用的時候,請使用懶載入的形式,參考demo中的BaseLazyFragment這個類

    第二種,當使用show()和hide()來控制Fragment顯示隱藏的時候,參考demo中的BaseTwoFragment這個類

    注意:

    • 2.2.7版本以後別忘了在Fragment的onDestroy方法裡銷燬沉浸式了,2.2.7版本之前不需要呼叫
       @Override
       protected void onDestroy() {
           super.onDestroy();
           if (mImmersionBar != null)
              mImmersionBar.destroy();  
       }
    
    • 以show()和hide()方式控制Fragment顯示隱藏,別忘了重寫onHiddenChanged方法,如下
         @Override
         public void onHiddenChanged(boolean hidden) {
             super.onHiddenChanged(hidden);
             if (!hidden && mImmersionBar != null)
                mImmersionBar.init();
         }
    
  • 在Activity使用ImmersionBar

    第一種,當結合viewpager使用的時候,請使用viewpager的addOnPageChangeListener的方法監聽沉浸式,參考demo中FragmentThreeActivity這個類

    第二種,當使用show()和hide()來控制Fragment顯示隱藏的時候,請在tab切換的時候使用ImmersionBar,參考demo中FragmentFourActivity這個類

  • 使用Fragment第三方框架Fragmentation實現沉浸式

    參考demo中FragmentFiveActivityBaseFiveFragment這個類

結合fragment使用.gif

  • 在Dialog中實現沉浸式,具體實現參考demo

在DialogFragment使用

   ImmersionBar.with(this, dialog).init();      

其他Dialog

 ImmersionBar.with(this, dialog, "flag")   //第三個引數是為當前Dialog加上標記,多個Dialog之間不可相同
                  .init();

注意:在dialog使用,當銷燬dialog同時,別忘了呼叫ImmersionBar的destroy方法了

 

結合dialog使用.gif

  • 圖片狀態列+彩色導航欄

     ImmersionBar.with(this)
             .transparentStatusBar()  //不寫也可以,預設就是透明色
             .navigationBarColor(R.color.colorPrimary)
             .init();
    

圖片狀態列+彩色導航欄.jpg

  • 全屏圖片

      ImmersionBar.with(this).transparentBar().init();
    

全屏圖片.jpg

  • 彩色狀態列+彩色導航欄

       ImmersionBar.with(this)
                .statusBarColor(R.color.colorPrimary)
                .navigationBarColor(R.color.btn8)
                .init();

彩色狀態列+彩色導航欄.jpg

  • 結合DrawerLayout使用

結合DrawerLayout使用.gif

  • 結合側滑返回使用

側滑返回.gif

  • 修改狀態列字型顏色為深色

       ImmersionBar.with(this).statusBarDarkFont(true).init();

修改狀態列字型顏色為深色.jpg

  • 設定狀態列和導航欄透明度

       ImmersionBar.with(this)
                .navigationBarColor(R.color.colorPrimary)
                .barAlpha(0.2f)
                .init();

設定狀態列和導航欄透明度.jpg

  • 隱藏狀態列

 ImmersionBar.with(this).hideBar(BarHide.FLAG_HIDE_STATUS_BAR).init();
  • 隱藏導航欄

  ImmersionBar.with(this).hideBar(BarHide.FLAG_HIDE_NAVIGATION_BAR).init();
  • 隱藏狀態列+導航欄

  ImmersionBar.with(this).hideBar(BarHide.FLAG_HIDE_BAR).init();
  • 恢復狀態列+導航欄

  ImmersionBar.with(this).hideBar(BarHide.FLAG_SHOW_BAR).init();
  • 解決EditText和軟鍵盤的問題

實現原理:監聽介面容器的layout變化,當發生變化時,通過檢查視窗可見區域高度,判斷鍵盤是否彈起,如果彈起,則修改容器bottom padding,也就是手動實現adjustResize效果,給鍵盤留出顯示空間。

   ImmersionBar.with(this)
               .keyboardEnable(true)  //解決軟鍵盤與底部輸入框衝突問題
               .init();
       或者
       // KeyboardPatch.patch(this).enable();
       或者,layout指的是當前佈局的根節點
       // KeyboardPatch.patch(this, layout).enable();

解決EditText和軟鍵盤的問題.gif

  • 當白色背景狀態列遇到不能改變狀態列字型為深色的裝置時,解決方案

        ImmersionBar.with(this)
                  .statusBarDarkFont(true, 0.2f)
                  .init();

白色背景狀態列.png

  • 特性總結

ImmersionBar除了這些特性之外,還有其他特性哦,這裡就不一一指出了,大家參考高階用法的註釋,可以去實現看看哦,下面就來分析原始碼吧

3、原始碼分析

   本庫採用類似建造者模式來完成,只為了方便大家更靈活的去設定狀態列和導航欄風格。實現沉浸式是分為兩塊,一塊是android5.0以上,一塊是android4.4,這兩塊實現原理完全不一樣,在講解原理之前先看幾個概念,下面需要用到
  • View.SYSTEM_UI_FLAG_VISIBLE:顯示狀態列,Activity不全屏顯示(恢復到有狀態的正常情況)。

  • View.INVISIBLE:隱藏狀態列,同時Activity會伸展全屏顯示。

  • View.SYSTEM_UI_FLAG_FULLSCREEN:Activity全屏顯示,且狀態列被隱藏覆蓋掉。

  • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:Activity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態遮住。

  • View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  • View.SYSTEM_UI_LAYOUT_FLAGS:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隱藏虛擬按鍵(導航欄)。有些手機會用虛擬按鍵來代替物理按鍵。

  • View.SYSTEM_UI_FLAG_LOW_PROFILE:狀態列顯示處於低能顯示狀態(low profile模式),狀態列上一些圖示顯示會被隱藏。

  • android 5.0以上核心程式碼

     Android自5.0起,為我們提供了設定狀態列和導航欄顏色的API,我們可以自己設定狀態列和導航欄的顏色。本框架在android5.0以上就是採用官方api完成的,網上關於5.0以上的實現基本都是這樣,在這裡就不多說了,在這裡只說一點,就是設定顏色的時候不是直接填入開發者傳入的顏色值,而是採用v4包下的ColorUtils.blendARGB()方法來設定,為什麼這樣設計呢?有些app的狀態列並不是和標題欄顏色相同,稍微有些色差,所以在這裡開發者只需要通過blendARGB()設定透明度就可以形成這種色差,而且還可以指定兩種顏色之間的色差值,方便大家,android4.4上亦是如此。請看以下程式碼:
    
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                int uiFlags =View.SYSTEM_UI_FLAG_LAYOUT_STABLE   //防止系統欄隱藏時內容區域大小發生變化 
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;  //Activity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態列遮住。
                if (mBarParams.fullScreen) {
                    uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; //Activity全屏顯示,但導航欄不會被隱藏覆蓋,導航欄依然可見,Activity底部佈局部分會被導航欄遮住。
                }
                mWindow.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);  //取消設定透明狀態列和導航欄
                mWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);  //需要設定這個才能設定狀態列顏色
                mWindow.setStatusBarColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                        mBarParams.statusBarColorTransform, mBarParams.statusBarAlpha));  //設定狀態列顏色
                mWindow.setNavigationBarColor(ColorUtils.blendARGB(mBarParams.navigationBarColor,
                        mBarParams.navigationBarColorTransform, mBarParams.navigationBarAlpha));  //設定導航欄顏色
                mWindow.getDecorView().setSystemUiVisibility(uiFlags); 把剛才設定的標記通過setSystemUiVisibility方法設定進去
            } 

  • android 4.4核心程式碼

       在4.4裡就沒有5.0以上這些api了,只能設定透明狀態列和導航欄,而且設定透明導航欄之後,底部佈局會被導航欄遮住,那怎麼辦呢?好吧,只能自己寫程式碼去實現啦。再說之前,說說我的一個思路吧。現在是2.x.x版本,在1.x.x版本的時候,4.4中實現沉浸式是引用大家非常熟悉的一個庫SystemBarTint(不推薦使用了,很久沒人維護了)來實現的,但是後來發現一個嚴重的問題,對於有導航欄的手機,設定導航欄顏色的時候,底部佈局會被導航欄遮住,除此之外還有一個小問題就是當用戶設定狀態列為透明色的時候,不能時刻改變bar的顏色值,are you kidding?既然出現這樣的問題,就想著怎麼去解決吧!就這樣,我乖乖去看看SystemBarTint的原始碼,哦!原理如此,發現SystemBarTint庫的實現就是在狀態列和導航欄的位置自定義了可以改變背景顏色的view,然後通過window.getDecorView()得到根佈局,把剛才建立的view新增進去,既然這樣,為何不自己也寫一個,順便把剛才說到導航欄的問題也解決一下呢。解決方法如下程式碼,在這裡通過註釋的方法向大家說明。這裡程式碼只是片段,不可以直接拷貝到自己的專案中
    
       if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            mWindow.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//透明狀態列
                mWindow.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);//透明導航欄,設定這個,如果有導航欄,底部佈局會被導航欄遮住
                setupStatusBarView(); //在根節點建立一個可以自定義顏色的狀態列建立一個假的狀態列
                if (mConfig.hasNavigtionBar())  //判斷是否存在導航欄
                    setupNavBarView();   //在根節點建立一個可以自定義顏色的導航欄
                // 解決android4.4有導航欄的情況下,activity底部被導航欄遮擋的問題
                if (mConfig.hasNavigtionBar() && !mBarParams.fullScreenTemp && !mBarParams.fullScreen) {
                    if (mConfig.isNavigationAtBottom()) //判斷導航欄是否在底部
                        mContentView.setPadding(0, 0, 0, mConfig.getNavigationBarHeight()); //有導航欄,獲得當前佈局的根節點,然後設定距離底部的padding值為導航欄的高度值
                    else
                        mContentView.setPadding(0, 0, mConfig.getNavigationBarWidth(), 0); //不在底部,設定距離右邊的padding值為導航欄的寬度值
                } else {
                    mContentView.setPadding(0, 0, 0, 0); //沒有導航欄,什麼都不做
                }
        }
/**
     * 在根節點建立一個可以自定義顏色的狀態列
     */
    private void setupStatusBarView() {
        if (mBarParams.statusBarView == null) {
            mBarParams.statusBarView = new View(mActivity);//建立一個view
        }
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, getStatusBarHeight(mActivity));
        params.gravity = Gravity.TOP; //把view設定在頂部
        if (!isNavigationAtBottom(mActivity)) {
            params.rightMargin = getNavigationBarWidth(mActivity); //橫屏的時候,距離右邊的距離
        }
        mBarParams.statusBarView.setLayoutParams(params);
        mBarParams.statusBarView.setBackgroundColor(ColorUtils.blendARGB(mBarParams.statusBarColor,
                mBarParams.statusBarColorTransform, mBarParams.statusBarAlpha));//設定view的顏色
        mBarParams.statusBarView.setVisibility(View.VISIBLE);
        ViewGroup viewGroup = (ViewGroup) mBarParams.statusBarView.getParent();
        if (viewGroup != null)
            viewGroup.removeView(mBarParams.statusBarView);
        mViewGroup.addView(mBarParams.statusBarView);
    }
/**
     * 在根節點建立一個可以自定義顏色的導航欄
     */
    private void setupNavBarView() {
        if (mBarParams.navigationBarView == null) {
            mBarParams.navigationBarView = new View(mActivity);  //建立一個view
        }
        FrameLayout.LayoutParams params;
        if (isNavigationAtBottom(mActivity)) { //判斷導航欄是否在底部
            params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, getNavigationBarHeight(mActivity));
            params.gravity = Gravity.BOTTOM; //如果在底部把view設定在導航欄的位置
        } else {
            params = new FrameLayout.LayoutParams(getNavigationBarWidth(mActivity), FrameLayout.LayoutParams.MATCH_PARENT);
            params.gravity = Gravity.END;  //不在底部,把view設定到佈局的結束位置
        }
        mBarParams.navigationBarView.setLayoutParams(params);
        if (!mBarParams.fullScreen && (mBarParams.navigationBarColorTransform == Color.TRANSPARENT)) {
            mBarParams.navigationBarView.setBackgroundColor(ColorUtils.blendARGB(mBarParams.navigationBarColor,
                    Color.BLACK, mBarParams.navigationBarAlpha));
        } else {
            mBarParams.navigationBarView.setBackgroundColor(ColorUtils.blendARGB(mBarParams.navigationBarColor,
                    mBarParams.navigationBarColorTransform, mBarParams.navigationBarAlpha));
        }
        mBarParams.navigationBarView.setVisibility(View.VISIBLE);
        ViewGroup viewGroup = (ViewGroup) mBarParams.navigationBarView.getParent();
        if (viewGroup != null)
            viewGroup.removeView(mBarParams.navigationBarView);
        mViewGroup.addView(mBarParams.navigationBarView);
    }
  • 狀態列字型顏色

     沉浸式原理說完了,在看看狀態列字型顏色怎麼去修改吧,在android 6.0以上系統為我們提供了相關的api來設定狀態列字型顏色,如下
    
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
           activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  }
   但是對於一些第三方rom包來說,系統api就沒辦法實現了,還好小米和魅族公開了各自的實現方法,如下:
  • flymeOS:
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
   boolean result = false;
   if (window != null) {
       try {
           WindowManager.LayoutParams lp = window.getAttributes();
           Field darkFlag = WindowManager.LayoutParams.class
                   .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
           Field meizuFlags = WindowManager.LayoutParams.class
                   .getDeclaredField("meizuFlags");
           darkFlag.setAccessible(true);
           meizuFlags.setAccessible(true);
           int bit = darkFlag.getInt(null);
           int value = meizuFlags.getInt(lp);
           if (dark) {
               value |= bit;
           } else {
               value &= ~bit;
           }
           meizuFlags.setInt(lp, value);
           window.setAttributes(lp);
           result = true;
       } catch (Exception e) {

       }
   }
   return result;
}
  • MIUI:
public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) {
   boolean result = false;
   if (window != null) {
       Class clazz = window.getClass();
       try {
           int darkModeFlag = 0;
           Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
           Field  field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
           darkModeFlag = field.getInt(layoutParams);
           Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
           if(dark){
               extraFlagField.invoke(window,darkModeFlag,darkModeFlag);//狀態列透明且黑色字型
           }else{
               extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字型
           }
           result=true;
       }catch (Exception e){

       }
   }
   return result;
}
  • 狀態列和導航欄的隱藏

     android 4.1以上支援狀態列和導航欄隱藏
    
private int hideBar(int uiFlags) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            switch (mBarParams.barHide) {
                case FLAG_HIDE_BAR: //隱藏狀態列和導航欄
                    uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.INVISIBLE;
                    break;
                case FLAG_HIDE_STATUS_BAR: //隱藏狀態列
                    uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.INVISIBLE;
                    break;
                case FLAG_HIDE_NAVIGATION_BAR://隱藏導航欄
                    uiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
                    break;
                case FLAG_SHOW_BAR://恢復顯示
                    uiFlags |= View.SYSTEM_UI_FLAG_VISIBLE;
                    break;
            }
        }
        return uiFlags | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    }

4、總結

   至此,ImmersionBar庫的用法與原理都講完了。網上關於沉浸式的介紹鋪天蓋地,但是很少有人把它們封裝起來,當開發者呼叫的時候還得自己去寫大量程式碼,消耗大家時間。寫這個庫的目的就是方便大家的開發,解決大家在沉浸式方面出現的問題。如果還有不懂得地方可以去demo裡看看,或者直接底下留言!

本文已獨家授權發表於微信公眾號:碼個蛋(微訊號:codeegg)