Android沉浸式狀態欄

Android沉浸式狀態欄

Android狀態欄默認是固定的黑底白字,這肯定是不被偉大的設計師所喜愛的,更有甚者,某些時候設計希望內容能夠延時到狀態欄底部(例如頭部是大圖的情況)。所幸的是隨著Android版本的迭代,開發者對狀態欄等控件有了更多的控制。Android一直在嘗試引入新的Api來滿足開發者的需求,但Api卻一直不夠完美,接口添加了很多,卻都不夠簡單或者說完美,算上第三方廠商的特色行為,怎一個“亂”字了得

Android完美的沉浸式需要多個接口配合使用才能完成,我們需要去了解android各個版本引入的Api的功能和局限性,因此這篇文章首先會介紹系統的一些接口,然后展示如何封裝一些接口用于實現沉浸式。

  • SystemUI
  • StatusBar顏色更改
  • fitSystemWindows
  • 一個完整的封裝

SystemUI

在Android2.3以前,對StatusBar的操作有兩個:StatusBar的顯示與隱藏、Activiy內容延伸到StatusBar下方(全局布局)。

// 全屏布局且隱藏狀態欄:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

// 全屏布局,不隱藏狀態欄:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 

| WindowManager.LayoutParams.FLAGLAYOUTNO_LIMITS);

在Android3.0中,View添加了一個重要的方法:setSystemUiVisibility(int),用于控制一些窗口裝飾元素的顯示,并添加了View.STATUS_BAR_VISIBLEView.STATUS_BAR_HIDDEN兩個Flag用于控制Status Bar的顯示與隱藏。

在Android4.0中,View.STATUS_BAR_VISIBLE改為View.SYSTEM_UI_FLAG_VISIBLE,View.STATUS_BAR_HIDDEN更名為View.SYSTEM_UI_FLAG_LOW_PROFILE。由于引進了NavigationBar,因此也添加了一個flag:SYSTEM_UI_FLAG_HIDE_NAVIGATION

  • View.SYSTEM_UI_FLAG_LOW_PROFILE: 同時影響StatusBar和NavigationBar,但并不會使得SystemUI消失,而只會使得背景很淺,并且去掉SystemUI的一些圖標或文字。
  • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION: 會隱藏NavigationBar,但是由于NavigationBar是非常重要的,因此只要有用戶交互,系統就會清除這個flag使NavigationBar就會再次出現。

在Android4.1中,又引入了以下幾個flag:

  • View.SYSTEM_UI_FLAG_FULLSCREEN: 這個標志與WindowManager.LayoutParams.FLAG_FULLSCREEN作用相同,但是如果你從屏幕下滑或者一些其它操作,會使得StatusBar重新顯示。
  • View.SYSTEM_UI_FLAG_LAYOUT_STABLE: 與其它flag配合使用,防止系統欄隱藏時內容區域發生變化。
  • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN: Activity全屏顯示,但狀態欄不會被隱藏覆蓋,狀態欄依然可見,Activity頂端布局部分會被狀態遮住
  • View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION: 使內容布局到NavigationBar之下,可以配合SYSTEM_UI_FLAG_HIDE_NAVIGATION使用防止跳動

在Android4.4(API 19)又增加了兩個flag:

  • View.SYSTEM_UI_FLAG_IMMERSIVE
  • View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY

這兩個flag主要是對SYSTEM_UI_FLAG_FULLSCREENSYSTEM_UI_FLAG_HIDE_NAVIGATION的修補。前文已經說過,在使用這兩個flag后,用戶的某些行為會使得系統強制清除這些flag。這并不是用戶想要的,因此配合View.SYSTEM_UI_FLAG_IMMERSIVEView.SYSTEM_UI_FLAG_IMMERSIVE_STICKY就可以阻止系統的強制清除行為。

View.SYSTEM_UI_FLAG_IMMERSIVE只作用與SYSTEM_UI_FLAG_FULLSCREEN,而View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY同時作用于兩個

綜上,我們可以給出全屏布局和隱藏狀態欄的新方案

//僅僅只是全屏布局:
//getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);

//全屏布局并且隱藏狀態欄與導航欄
getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_FULLSCREEN
    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);

在Android4.4還為WindowManager.LayoutParams添加了兩個flag:

  • FLAG_TRANSLUCENT_STATUS: 當使用這個flag時SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN會被自動添加
  • FLAG_TRANSLUCENT_NAVIGATION:當使用這這個個flag時SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION會被自動添加。

StatusBar顏色更改

StatusBar的顏色更改分為兩部分,一個是背景顏色的修改,一個是字體顏色的修改。

首先先說說背景顏色的修改,在Android 5.0之前,狀態欄顏色并不可定制,5.0之后才可定制。首先,我們可以在主題里通過colorPrimaryDark來指定背景色,其次,我們可以調用 window.setStatusBarColor(@ColorInt int color) 來修改狀態欄顏色,但是讓這個方法生效有一個前提條件:

你必須給window添加FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS并且取消FLAG_TRANSLUCENT_STATUS

此外,設置FLAG_TRANSLUCENT_STATUS也會影響到StatusBar的背景色,但并沒有固定的表現:

  • 對于6.0以上的機型,設置此flage會使得StatusBar完全透明
  • 對于5.x的機型,大部分是使背景色半透明,小米和魅族以及其它少數機型會全透明
  • 對于4.4的機型,小米和魅族是透明色,而其它系統上就只是黑色到透明色的漸變。

我們知道了改背景色后,我們再來看看字體和圖標顏色的更改。默認字體和圖標是白色,如果在淺色背景上就會看不到狀態欄信息了,因此體驗會很糟糕。但可惜的是android6.0才官方支持更改字體和圖標的顏色。

在Android6以后,我們只要給SystemUI加上SYSTEM_UI_FLAG_LIGHT_STATUS_BAR這個flag,就可以讓字體和圖標變為黑色。雖然官方已經支持了,但國內有些機型的版本號確實是6.0,但并不能更改字體和圖標顏色,例如聯想的ZUK Z1機型

當然,國內的魅族和小米走在前沿,從Android4.4開始就已經更改字體和圖標顏色了,但并沒有直接的接口用,必須通過反射的方式去更改字體顏色

針對小米的方案:

/**
 * 設置狀態欄字體圖標為深色,需要 MIUIV6 以上
 *
 * @param window 需要設置的窗口
 * @param dark   是否把狀態欄字體及圖標顏色設置為深色
 * @return boolean 成功執行返回 true
 */
public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) {
    boolean result = false;
    if (window != null) {
        Class clazz = window.getClass();
        try {
            int darkModeFlag;
            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;
}

針對魅族的方案:

 /**
 * 設置狀態欄圖標為深色和魅族特定的文字風格
 * 可以用來判斷是否為 Flyme 用戶
 *
 * @param window 需要設置的窗口
 * @param dark   是否把狀態欄字體及圖標顏色設置為深色
 * @return boolean 成功執行返回true
 */
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;
}

對于小米魅族除外的Android5.x的機器,不能改字體和圖標顏色,如果app是淺色皮膚,那么我們就只能給StatusBar設置半透明的背景了,并且FLAG_TRANSLUCENT_STATUS并不可靠(前文已說,表現不一定是半透明背景)

fitSystemWindows

我們首先探討了內容布局是否全屏以及狀態欄的顯示與隱藏,其次我們探討了狀態欄顏色的修改問題。那如果我們全屏布局并且顯示透明狀態欄的時候會怎樣?

狀態欄與內容會重疊。這既是我們想要的效果,也是我們不想要的內容。如果APP頂部時高斯模糊的圖片,與狀態欄重疊是設計師希望看到的效果;但是,如果ActionBar和狀態欄重疊了,那可就不好看了。 所以重疊與不重疊完全看業務,而庫的封裝者則需要告訴業務方,如何才能不重疊。

這個時候就是fitSystemWindows出場的時候了。

我們可以給view設置fitSystemWindows屬性,其是一個bool值。其既可以在xml里直接設置android:fitsSystemWindows="true",也可以通過View#setFitsSystemWindows(boolean fitSystemWindows)在java代碼中設置。不過這一步也僅僅只是設置了一個flag。

Android系統組件例如狀態欄、NavBar、鍵盤所占據的空間稱為界面的WindowInsets,Android系統會在特定的時機從根View派發WindowInsets,如果View的fitSystemWindows標志位被設為true的話,WindowInsets會傳遞給下列幾個方法:

  1. fitSystemWindows(Rect insets): 這個是老版本提供的接口,現在已經被棄用,僅用于API 19
  2. onApplyWindowInsets(WindowInsets insets): 這應該是標準的方式了,然而在魅藍M1上竟然會出現找不到WindowInsets這個類的crash
  3. 使用ViewCompat.setOnApplyWindowInsetsListener添加的Listener: 這種setListener的方式比較靈活,并且傳值是WindowInsetsCompat類型,在魅藍M1等機型都可以跑通,是上乘之選。

此外有幾個關鍵點需要重點關注:

  1. 一旦有一個View消耗了WindowInsets,那么WindowInsets的dispatch就結束了。所以一般只在Activity的最外層View調用setFitsSystemWindows(true)
  2. 系統處理WindowInsets的手段本質是設置padding,因此這會讓你View原本的padding失效
  3. 一般而言,只有一個View消耗WindowInsets,但這是系統行為,我們可以在onApplyWindowInsets里主動調用dispatchApplyWindowInsets使得其可以繼續傳遞。

第三點的意義在于,如果我們需要多個View受WindowInsets影響時,我們可以自己去傳遞WindowInsets,一般封裝者也會提供一個WindowInsetsLayout, 讓直接子元素的fitSystemWindows都生效。@XiNGRZ在Mantou Earth有一個很好的實現(點我查看)。使用這個Layout可以滿足大部分需求,但也存在一個小漏洞:使用onApplyWindowInsets在魅藍M1上會crash(前文已經指出原因)

業務上可能會對fitSystemWindows有更復雜的應用,很多時候是由于歷史業務的原因導致大大小小的坑,這個時候就需要我們很好的把握fitSystemWindows,隨機應變,自由適配WindowInsets了。

一個完整的封裝

基于上述的種種討論,我認為一個良好的封裝應該提供三個方面的接口:全屏布局+ 狀態欄透明(5.x半透明)、 更改狀態欄顏色、 一個WindowInsetsLayout。

下面看一下QMUI(內部Android UI庫)的實現:

/**
 * 沉浸式狀態欄
 * 支持 4.4 以上版本的 MIUI 和 Flyme,以及 5.0 以上版本的其他 Android
 *
 * @param activity
 */
@TargetApi(19)
public static void translucent(Activity activity, @ColorInt int colorOn5x) {
    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
        // 版本小于4.4,絕對不考慮沉浸式
        return;
    }
    // 小米和魅族4.4 以上版本支持沉浸式
    if (QMUIDeviceHelper.isMeizu() || QMUIDeviceHelper.isMIUI()) {
        Window window = activity.getWindow();
        window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        return;
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && supportTransclentStatusBar6()) {
            // android 6以后可以改狀態欄字體顏色,因此可以自行設置為透明
            // ZUK Z1是個另類,自家應用可以實現字體顏色變色,但沒開放接口
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        } else {
            // android 5不能修改狀態欄字體顏色,因此直接用FLAG_TRANSLUCENT_STATUS,nexus表現為半透明
            // update: 部分手機運用FLAG_TRANSLUCENT_STATUS時背景不是半透明而是沒有背景了。。。。。
            // window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

            // 采取setStatusBarColor的方式,部分機型不支持,那就純黑了,保證狀態欄圖標可見
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(colorOn5x);
        }
//        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//            // android4.4的默認是從上到下黑到透明,我們的背景是白色,很難看,因此只做魅族和小米的
//        } else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1){
//            // 如果app 為白色,需要更改狀態欄顏色,因此不能讓19一下支持透明狀態欄
//            Window window = activity.getWindow();
//            Integer transparentValue = getStatusBarAPITransparentValue(activity);
//            if(transparentValue != null) {
//                window.getDecorView().setSystemUiVisibility(transparentValue);
//            }
    }
}

然后是更改狀態欄的顏色:

/**
 * 設置狀態欄黑色字體圖標,
 * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
 *
 * @param activity 需要被處理的 Activity
 */
public static void setStatusBarLightMode(Activity activity) {
    if (mStatuBarType != STATUSBAR_TYPE_DEFAULT) {
        setStatusBarLightMode(activity, mStatuBarType);
        return;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        if (MIUISetStatusBarLightMode(activity.getWindow(), true)) {
            mStatuBarType = 1;
        } else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
            mStatuBarType = 2;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = activity.getWindow();
            View decorView = window.getDecorView();
            int systemUi = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            systemUi = changeStatusBarModeRetainFlag(window, systemUi);
            decorView.setSystemUiVisibility(systemUi);
            mStatuBarType = 3;
        }
    }
}

/**
 * 已知系統類型時,設置狀態欄黑色字體圖標。
 * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
 *
 * @param activity 需要被處理的 Activity
 * @param type     StatusBar 類型,對應不同的系統
 */
private static void setStatusBarLightMode(Activity activity, @StatusBarType int type) {
    if (type == STATUSBAR_TYPE_MIUI) {
        MIUISetStatusBarLightMode(activity.getWindow(), true);
    } else if (type == STATUSBAR_TYPE_FLYME) {
        FlymeSetStatusBarLightMode(activity.getWindow(), true);
    } else if (type == STATUSBAR_TYPE_ANDROID6) {
        Window window = activity.getWindow();
        View decorView = window.getDecorView();
        int systemUi = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
        systemUi = changeStatusBarModeRetainFlag(window, systemUi);
        decorView.setSystemUiVisibility(systemUi);
    }

}

/**
 * 設置狀態欄白色字體圖標
 * 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
 */
public static void setStatusBarDarkMode(Activity activity) {
    if (mStatuBarType == STATUSBAR_TYPE_DEFAULT) {
        // 默認狀態,不需要處理
        return;
    }
    if (mStatuBarType == STATUSBAR_TYPE_MIUI) {
        MIUISetStatusBarLightMode(activity.getWindow(), false);
    } else if (mStatuBarType == STATUSBAR_TYPE_FLYME) {
        FlymeSetStatusBarLightMode(activity.getWindow(), false);
    } else if (mStatuBarType == STATUSBAR_TYPE_ANDROID6) {
        Window window = activity.getWindow();
        View decorView = window.getDecorView();
        int systemUi = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        systemUi = changeStatusBarModeRetainFlag(window, systemUi);
        decorView.setSystemUiVisibility(systemUi);
    }
}

/**
 * 每次設置SystemUiVisibility要保證其它必須的flag不能丟
 */
private static int changeStatusBarModeRetainFlag(Window window, int out) {
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_FULLSCREEN);
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    out = retainSystemUiFlag(window, out, View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    return out;
}

public static int retainSystemUiFlag(Window window, int out, int type) {
    int now = window.getDecorView().getSystemUiVisibility();
    if ((now & type) == type) {
        out |= type;
    }
    return out;
}

最后是QMUIWindowInsetLayout,這個只是對XiNGRZ的代碼作了一些小改動:

public class QMUIWindowInsetLayout extends FrameLayout {

    public QMUIWindowInsetLayout(Context context) {
        this(context, null);
    }

    public QMUIWindowInsetLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public QMUIWindowInsetLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ViewCompat.setOnApplyWindowInsetsListener(this,
                new android.support.v4.view.OnApplyWindowInsetsListener() {
                    @Override
                    public WindowInsetsCompat onApplyWindowInsets(View v,
                                                                  WindowInsetsCompat insets) {
                        return setWindowInsets(insets);
                    }
                });
    }

    private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
        if (Build.VERSION.SDK_INT >= 21 && insets.hasSystemWindowInsets()) {
            if (applySystemWindowInsets21(insets)) {
                return insets.consumeSystemWindowInsets();
            }
        }
        return insets;
    }

    @SuppressWarnings("deprecation")
    @Override
    protected boolean fitSystemWindows(Rect insets) {
        if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
            return applySystemWindowInsets19(insets);
        }
        return super.fitSystemWindows(insets);
    }

    @SuppressWarnings("deprecation")
    @TargetApi(19)
    private boolean applySystemWindowInsets19(Rect insets) {
        boolean consumed = false;

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (!child.getFitsSystemWindows()) {
                continue;
            }

            Rect childInsets = new Rect(insets);
            computeInsetsWithGravity(child, childInsets);

            child.setPadding(childInsets.left, childInsets.top, childInsets.right, childInsets.bottom);

            consumed = true;
        }

        return consumed;
    }

    @TargetApi(21)
    private boolean applySystemWindowInsets21(WindowInsetsCompat insets) {
        boolean consumed = false;

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);

            if (!child.getFitsSystemWindows()) {
                continue;
            }

            Rect childInsets = new Rect(
                    insets.getSystemWindowInsetLeft(),
                    insets.getSystemWindowInsetTop(),
                    insets.getSystemWindowInsetRight(),
                    insets.getSystemWindowInsetBottom());

            computeInsetsWithGravity(child, childInsets);
            ViewCompat.dispatchApplyWindowInsets(child, insets.replaceSystemWindowInsets(childInsets));

            consumed = true;
        }

        return consumed;
    }

    @SuppressLint("RtlHardcoded")
    private void computeInsetsWithGravity(View view, Rect insets) {
        LayoutParams lp = (LayoutParams) view.getLayoutParams();

        int gravity = lp.gravity;

        /**
         * 因為該方法執行時機早于 FrameLayout.layoutChildren,
         * 而在 {FrameLayout#layoutChildren} 中當 gravity == -1 時會設置默認值為 Gravity.TOP | Gravity.LEFT,
         * 所以這里也要同樣設置
         */
        if (gravity == -1) {
            gravity = Gravity.TOP | Gravity.LEFT;
        }

        if (lp.width != LayoutParams.MATCH_PARENT) {
            int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
            switch (horizontalGravity) {
                case Gravity.LEFT:
                    insets.right = 0;
                    break;
                case Gravity.RIGHT:
                    insets.left = 0;
                    break;
            }
        }

        if (lp.height != LayoutParams.MATCH_PARENT) {
            int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
            switch (verticalGravity) {
                case Gravity.TOP:
                    insets.bottom = 0;
                    break;
                case Gravity.BOTTOM:
                    insets.top = 0;
                    break;
            }
        }
    }

}

目前這套方案用于微信讀書,應該是相當穩定的方案了,使用較為靈活。
轉載自:http://blog.cgsdream.org/2017/03/16/android-translcent-statusbar/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377