Android 水滴屏、全屏适配

Android 水滴屏、全屏适配

何谓刘海屏?何谓水滴屏?

上述两种屏幕都可以统称为刘海屏,不过对于右侧较小的刘海,业界一般称为水滴屏或美人尖。

目前国内流行的手机厂商主要有:vivo、oppo、华为、小米。各厂商对刘海屏的适配都大不相同,各自有各自对刘海屏的适配API,具体的适配方法可以阅读相应的官网:

VIVO: https://dev.vivo.com.cn/documentCenter/doc/103

OPPO: https://open.oppomobile.com/wiki/doc#id=10159

小米: https://dev.mi.com/console/doc/detail?pId=1293

华为: https://developer.huawei.com/consumer/cn/devservice/doc/50114?from=timeline

下面介绍几种适配方法:

1.StatusBarUtil工具类

1.1 StatusBarUtil工具类

public class StatusBarUtil {
    //透明度
    public static final int DEFAULT_STATUS_BAR_ALPHA = 112;
    private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;
    private static final int FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view;
    private static final int TAG_KEY_HAVE_SET_OFFSET = -123;
    /**
     * 设置状态栏颜色
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    public static void setColor(Activity activity, @ColorInt int color) {
        setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);
    }
    /**
     * 设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
            if (fakeStatusBarView != null) {
                if (fakeStatusBarView.getVisibility() == View.GONE) {
                    fakeStatusBarView.setVisibility(View.VISIBLE);
                }
                fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            } else {
                decorView.addView(createStatusBarView(activity, color, statusBarAlpha));
            }
            setRootView(activity);
        }
    }
    /**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     */
    public static void setColorForSwipeBack(Activity activity, int color) {
        setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA);
    }
    /**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColorForSwipeBack(Activity activity, @ColorInt int color,
                                            @IntRange(from = 0, to = 255) int statusBarAlpha) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content));
            View rootView = contentView.getChildAt(0);
            int statusBarHeight = getStatusBarHeight(activity);
            if (rootView != null && rootView instanceof CoordinatorLayout) {
                final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView;
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    coordinatorLayout.setFitsSystemWindows(false);
                    contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                    boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight;
                    if (isNeedRequestLayout) {
                        contentView.setPadding(0, statusBarHeight, 0, 0);
                        coordinatorLayout.post(new Runnable() {
                            @Override
                            public void run() {
                                coordinatorLayout.requestLayout();
                            }
                        });
                    }
                } else {
                    coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                }
            } else {
                contentView.setPadding(0, statusBarHeight, 0, 0);
                contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            }
            setTransparentForWindow(activity);
        }
    }
    /**
     * 设置状态栏纯色 不加半透明效果
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    public static void setColorNoTranslucent(Activity activity, @ColorInt int color) {
        setColor(activity, color, 0);
    }
    /**
     * 设置状态栏纯色 不加半透明效果 字体为黑色
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    public static void setColorNoTranslucentLightMode(Activity activity, @ColorInt int color) {
        setColor(activity, color, 0);
        //setLightMode(activity);
    }
    /**
     * 设置状态栏颜色(5.0以下无半透明效果,不建议使用)
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    @Deprecated
    public static void setColorDiff(Activity activity, @ColorInt int color) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return;
        }
        transparentStatusBar(activity);
        ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
        // 移除半透明矩形,以免叠加
        View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
        if (fakeStatusBarView != null) {
            if (fakeStatusBarView.getVisibility() == View.GONE) {
                fakeStatusBarView.setVisibility(View.VISIBLE);
            }
            fakeStatusBarView.setBackgroundColor(color);
        } else {
            contentView.addView(createStatusBarView(activity, color));
        }
        setRootView(activity);
    }
    /**
     * 使状态栏半透明
     * 

* 适用于图片作为背景的界面,此时需要图片填充到状态栏 * * @param activity 需要设置的activity */ public static void setTranslucent(Activity activity) { setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA); } /** * 使状态栏半透明 *

* 适用于图片作为背景的界面,此时需要图片填充到状态栏 * * @param activity 需要设置的activity * @param statusBarAlpha 状态栏透明度 */ public static void setTranslucent(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } setTransparent(activity); addTranslucentView(activity, statusBarAlpha); } /** * 针对根布局是 CoordinatorLayout, 使状态栏半透明 *

* 适用于图片作为背景的界面,此时需要图片填充到状态栏 * * @param activity 需要设置的activity * @param statusBarAlpha 状态栏透明度 */ public static void setTranslucentForCoordinatorLayout(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } transparentStatusBar(activity); addTranslucentView(activity, statusBarAlpha); } /** * 设置状态栏全透明 * * @param activity 需要设置的activity */ public static void setTransparent(Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } transparentStatusBar(activity); setRootView(activity); } /** * 使状态栏透明(5.0以上半透明效果,不建议使用) *

* 适用于图片作为背景的界面,此时需要图片填充到状态栏 * * @param activity 需要设置的activity */ @Deprecated public static void setTranslucentDiff(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 设置状态栏透明 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); setRootView(activity); } } /** * 为DrawerLayout 布局设置状态栏变色 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout * @param color 状态栏颜色值 */ public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA); } /** * 为DrawerLayout 布局设置状态栏颜色,纯色 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout * @param color 状态栏颜色值 */ public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { setColorForDrawerLayout(activity, drawerLayout, color, 0); } /** * 为DrawerLayout 布局设置状态栏颜色,纯色 字体为黑色 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout * @param color 状态栏颜色值 */ public static void setColorNoTranslucentForDrawerLayoutLightMode(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { setColorForDrawerLayout(activity, drawerLayout, color, 0); setLightMode(activity); } /** * 为DrawerLayout 布局设置状态栏变色 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout * @param color 状态栏颜色值 * @param statusBarAlpha 状态栏透明度 */ public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(Color.TRANSPARENT); } else { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } // 生成一个状态栏大小的矩形 // 添加 statusBarView 到布局中 ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); if (fakeStatusBarView != null) { if (fakeStatusBarView.getVisibility() == View.GONE) { fakeStatusBarView.setVisibility(View.VISIBLE); } fakeStatusBarView.setBackgroundColor(color); } else { contentLayout.addView(createStatusBarView(activity, color), 0); } // 内容布局不是 LinearLayout 时,设置padding top if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { contentLayout.getChildAt(1) .setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(), contentLayout.getPaddingRight(), contentLayout.getPaddingBottom()); } // 设置属性 setDrawerLayoutProperty(drawerLayout, contentLayout); addTranslucentView(activity, statusBarAlpha); } /** * 设置 DrawerLayout 属性 * * @param drawerLayout DrawerLayout * @param drawerLayoutContentLayout DrawerLayout 的内容布局 */ private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) { ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); drawerLayout.setFitsSystemWindows(false); drawerLayoutContentLayout.setFitsSystemWindows(false); drawerLayoutContentLayout.setClipToPadding(true); drawer.setFitsSystemWindows(false); } /** * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用) * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout * @param color 状态栏颜色值 */ @Deprecated public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 生成一个状态栏大小的矩形 ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); if (fakeStatusBarView != null) { if (fakeStatusBarView.getVisibility() == View.GONE) { fakeStatusBarView.setVisibility(View.VISIBLE); } fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA)); } else { // 添加 statusBarView 到布局中 contentLayout.addView(createStatusBarView(activity, color), 0); } // 内容布局不是 LinearLayout 时,设置padding top if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); } // 设置属性 setDrawerLayoutProperty(drawerLayout, contentLayout); } } /** * 为 DrawerLayout 布局设置状态栏透明 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout */ public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA); } /** * 为 DrawerLayout 布局设置状态栏透明 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout */ public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @IntRange(from = 0, to = 255) int statusBarAlpha) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } setTransparentForDrawerLayout(activity, drawerLayout); addTranslucentView(activity, statusBarAlpha); } /** * 为 DrawerLayout 布局设置状态栏透明 * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout */ public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(Color.TRANSPARENT); } else { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); // 内容布局不是 LinearLayout 时,设置padding top if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); } // 设置属性 setDrawerLayoutProperty(drawerLayout, contentLayout); } /** * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用) * * @param activity 需要设置的activity * @param drawerLayout DrawerLayout */ @Deprecated public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 设置状态栏透明 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 设置内容布局属性 ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); contentLayout.setFitsSystemWindows(true); contentLayout.setClipToPadding(true); // 设置抽屉布局属性 ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); vg.setFitsSystemWindows(false); // 设置 DrawerLayout 属性 drawerLayout.setFitsSystemWindows(false); } } /** * 为头部是 ImageView 的界面设置状态栏全透明 * * @param activity 需要设置的activity * @param needOffsetView 需要向下偏移的 View */ public static void setTransparentForImageView(Activity activity, View needOffsetView) { setTranslucentForImageView(activity, 0, needOffsetView); } /** * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度) * * @param activity 需要设置的activity * @param needOffsetView 需要向下偏移的 View */ public static void setTranslucentForImageView(Activity activity, View needOffsetView) { setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); } /** * 为头部是 ImageView 的界面设置状态栏透明 * * @param activity 需要设置的activity * @param statusBarAlpha 状态栏透明度 * @param needOffsetView 需要向下偏移的 View */ public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, View needOffsetView) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } setTransparentForWindow(activity); addTranslucentView(activity, statusBarAlpha); if (needOffsetView != null) { Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET); if (haveSetOffset != null && (Boolean) haveSetOffset) { return; } ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams(); layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity), layoutParams.rightMargin, layoutParams.bottomMargin); needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true); } } /** * 为 fragment 头部是 ImageView 的设置状态栏透明 * * @param activity fragment 对应的 activity * @param needOffsetView 需要向下偏移的 View */ public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) { setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); } /** * 为 fragment 头部是 ImageView 的设置状态栏透明 * * @param activity fragment 对应的 activity * @param needOffsetView 需要向下偏移的 View */ public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) { setTranslucentForImageViewInFragment(activity, 0, needOffsetView); } /** * 为 fragment 头部是 ImageView 的设置状态栏透明 * * @param activity fragment 对应的 activity * @param statusBarAlpha 状态栏透明度 * @param needOffsetView 需要向下偏移的 View */ public static void setTranslucentForImageViewInFragment(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, View needOffsetView) { setTranslucentForImageView(activity, statusBarAlpha, needOffsetView); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { clearPreviousSetting(activity); } } /** * 隐藏伪状态栏 View * * @param activity 调用的 Activity */ public static void hideFakeStatusBarView(Activity activity) { ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); if (fakeStatusBarView != null) { fakeStatusBarView.setVisibility(View.GONE); } View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); if (fakeTranslucentView != null) { fakeTranslucentView.setVisibility(View.GONE); } } @TargetApi(Build.VERSION_CODES.M) public static void setLightMode(Activity activity) { setMIUIStatusBarDarkIcon(activity, true); setMeizuStatusBarDarkIcon(activity, true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } } @TargetApi(Build.VERSION_CODES.M) public static void setDarkMode(Activity activity) { setMIUIStatusBarDarkIcon(activity, false); setMeizuStatusBarDarkIcon(activity, false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } } /** * 修改 MIUI V6 以上状态栏颜色 */ private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { Class clazz = activity.getWindow().getClass(); try { @SuppressLint("PrivateApi") Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); int darkModeFlag = field.getInt(layoutParams); Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag); } catch (Exception e) { //e.printStackTrace(); } } /** * 修改魅族状态栏字体颜色 Flyme 4.0 */ private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { try { WindowManager.LayoutParams lp = activity.getWindow().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 (darkIcon) { value |= bit; } else { value &= ~bit; } meizuFlags.setInt(lp, value); activity.getWindow().setAttributes(lp); } catch (Exception e) { //e.printStackTrace(); } } /// @TargetApi(Build.VERSION_CODES.KITKAT) private static void clearPreviousSetting(Activity activity) { ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); if (fakeStatusBarView != null) { decorView.removeView(fakeStatusBarView); ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setPadding(0, 0, 0, 0); } } /** * 添加半透明矩形条 * * @param activity 需要设置的 activity * @param statusBarAlpha 透明值 */ private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); if (fakeTranslucentView != null) { if (fakeTranslucentView.getVisibility() == View.GONE) { fakeTranslucentView.setVisibility(View.VISIBLE); } fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0)); } else { contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha)); } } /** * 生成一个和状态栏大小相同的彩色矩形条 * * @param activity 需要设置的 activity * @param color 状态栏颜色值 * @return 状态栏矩形条 */ private static View createStatusBarView(Activity activity, @ColorInt int color) { return createStatusBarView(activity, color, 0); } /** * 生成一个和状态栏大小相同的半透明矩形条 * * @param activity 需要设置的activity * @param color 状态栏颜色值 * @param alpha 透明值 * @return 状态栏矩形条 */ private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) { // 绘制一个和状态栏一样高的矩形 View statusBarView = new View(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID); return statusBarView; } /** * 设置根布局参数 */ private static void setRootView(Activity activity) { ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content); for (int i = 0, count = parent.getChildCount(); i < count; i++) { View childView = parent.getChildAt(i); if (childView instanceof ViewGroup) { childView.setFitsSystemWindows(true); ((ViewGroup) childView).setClipToPadding(true); } } } /** * 设置透明 */ private static void setTransparentForWindow(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().setStatusBarColor(Color.TRANSPARENT); activity.getWindow() .getDecorView() .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow() .setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } /** * 使状态栏透明 */ @TargetApi(Build.VERSION_CODES.KITKAT) private static void transparentStatusBar(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); activity.getWindow().setStatusBarColor(Color.TRANSPARENT); } else { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } /** * 创建半透明矩形 View * * @param alpha 透明值 * @return 半透明 View */ private static View createTranslucentStatusBarView(Activity activity, int alpha) { // 绘制一个和状态栏一样高的矩形 View statusBarView = new View(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID); return statusBarView; } /** * 获取状态栏高度 * * @param context context * @return 状态栏高度 */ public static int getStatusBarHeight(Context context) { // 获得状态栏高度 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); return context.getResources().getDimensionPixelSize(resourceId); } /*** * 只能作用于activity */ public static void setTaskBarColored(Activity context, int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window w = context.getWindow(); w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //status bar height int statusBarHeight = getStatusBarHeight(context); View view = new View(context); view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); view.getLayoutParams().height = statusBarHeight; ((ViewGroup) w.getDecorView()).addView(view); //view.setBackgroundColor(context.getResources().getColor(R.color.colorPrimaryTaskBar)); view.setBackgroundColor(color); } } /** * 计算状态栏颜色 * * @param color color值 * @param alpha alpha值 * @return 最终的状态栏颜色 */ private static int calculateStatusColor(@ColorInt int color, int alpha) { if (alpha == 0) { return color; } float a = 1 - alpha / 255f; int red = color >> 16 & 0xff; int green = color >> 8 & 0xff; int blue = color & 0xff; red = (int) (red * a + 0.5); green = (int) (green * a + 0.5); blue = (int) (blue * a + 0.5); return 0xff << 24 | red << 16 | green << 8 | blue; } /*** * Android设置背景图延伸到状态栏 * https://www.codenong.com/cs106491245/ * @param context */ public static void transparentStatusBarForImage(Activity context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.0 全透明实现 //getWindow.setStatusBarColor(Color.TRANSPARENT) Window window = context.getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //4.4 全透明状态栏 context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } // 设置状态栏文字颜色为黑色 public static void setStatusBarLightMode(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { View decorView = activity.getWindow().getDecorView(); int systemUiVisibility = decorView.getSystemUiVisibility(); decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } // 设置状态栏文字颜色为白色 public static void setStatusBarDarkMode(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { View decorView = activity.getWindow().getDecorView(); int systemUiVisibility = decorView.getSystemUiVisibility(); decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } // 设置状态栏(纯)颜色 public static void setStatusBarColor(Activity activity, int colorResId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(activity.getResources().getColor(colorResId)); } } public static void setStatusBarColor(Dialog dialog, int colorResId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = dialog.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(dialog.getContext().getResources().getColor(colorResId)); } } /*** * 设置dialog的状态栏的字体颜色 * @param dialog DialogFragment */ public static void setStatusBarLightMode(Dialog dialog) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { View decorView = dialog.getWindow().getDecorView(); int systemUiVisibility = decorView.getSystemUiVisibility(); decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } }

1.2 AndroidManifest.xml

1.3 ids.xml

  

2.NotchScreenTool第三方库

2.1 代码

// 支持显示到刘海区域
NotchScreenManager.getInstance().setDisplayInNotch(this);
// 获取刘海屏信息
NotchScreenManager.getInstance().getNotchInfo(this, new INotchScreen.NotchScreenCallback() { @Override
    public void onResult(INotchScreen.NotchScreenInfo notchScreenInfo) { Log.i(TAG, "Is this screen notch? " + notchScreenInfo.hasNotch);
        if (notchScreenInfo.hasNotch) { for (Rect rect : notchScreenInfo.notchRects) { Log.i(TAG, "notch screen Rect =  " + rect.toShortString());
            }
        }
    }
});

2.2 AndroidManifest.xml

在 标签中增加属性:android:resizeableActivity="false" 或者不设置

同时在节点下增加一个meta-data标签:

   

2.3 githup

githup

3.ImmersionBar第三方库

3.1 依赖

// 基础依赖包,必须要依赖
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2'
// kotlin扩展(可选)
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.2'
// fragment快速实现(可选)已废弃
implementation 'com.geyifeng.immersionbar:immersionbar-components:3.2.2'

3.2 关于全面屏与刘海

关于全面屏

在manifest加入如下配置,四选其一,或者都写

① 升级targetSdkVersion为25以上版本,现在基本都是25以上了,所以以下三个没有必要配置了

② 在manifest的Application节点中加入

 android:resizeableActivity="true"

③ 在manifest的Application节点中加入

 android:maxAspectRatio="2.4"

④ 在manifest的Application节点下加入

 
关于刘海屏

在manifest的Application节点下加入,vivo和oppo没有找到相关配置信息

    

3.3 Api详解

  • 基础用法

    ImmersionBar.with(this).init();
    
  • 高级用法(每个参数的意义)

     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)   //状态栏字体是深色,不写默认为亮色
                 .navigationBarDarkIcon(true) //导航栏图标是深色,不写默认为亮色
                 .autoDarkModeEnable(true) //自动状态栏字体和导航栏图标变色,必须指定状态栏颜色和导航栏颜色才可以自动变色哦
                 .autoStatusBarDarkModeEnable(true,0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦
                 .autoNavigationBarDarkModeEnable(true,0.2f) //自动导航栏图标变色,必须指定导航栏颜色才可以自动变色哦
                 .flymeOSStatusBarFontColor(R.color.btn3)  //修改flyme OS状态栏字体颜色
                 .fullScreen(true)      //有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏
                 .hideBar(BarHide.FLAG_HIDE_BAR)  //隐藏状态栏或导航栏或两者,不写默认不隐藏
                 .addViewSupportTransformColor(toolbar)  //设置支持view变色,可以添加多个view,不指定颜色,默认和状态栏同色,还有两个重载方法
                 .titleBar(view)    //解决状态栏和布局重叠问题,任选其一
                 .titleBarMarginTop(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支持
                 .navigationBarEnable(true)   //是否可以修改导航栏颜色,默认为true
                 .navigationBarWithKitkatEnable(true)  //是否可以修改安卓4.4和emui3.x手机导航栏颜色,默认为true
                 .navigationBarWithEMUI3Enable(true) //是否可以修改emui3.x手机导航栏颜色,默认为true
                 .keyboardEnable(true)  //解决软键盘与底部输入框冲突问题,默认为false,还有一个重载方法,可以指定软键盘mode
                 .keyboardMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)  //单独指定软键盘模式
                 .setOnKeyboardListener(new OnKeyboardListener() {    //软键盘监听回调,keyboardEnable为true才会回调此方法
                       @Override
                       public void onKeyboardChange(boolean isPopup, int keyboardHeight) {
                           LogUtils.e(isPopup);  //isPopup为true,软键盘弹出,为false,软键盘关闭
                       }
                  })
                 .setOnNavigationBarListener(onNavigationBarListener) //导航栏显示隐藏监听,目前只支持华为和小米手机
                 .setOnBarListener(OnBarListener) //第一次调用和横竖屏切换都会触发,可以用来做刘海屏遮挡布局控件的问题
                 .addTag("tag")  //给以上设置的参数打标记
                 .getTag("tag")  //根据tag获得沉浸式参数
                 .reset()  //重置所以沉浸式参数
                 .init();  //必须调用方可应用以上所配置的参数
    
    在Activity中实现沉浸式
    • java用法

       ImmersionBar.with(this).init();
      
    • kotlin用法

       immersionBar {
           statusBarColor(R.color.colorPrimary) 
           navigationBarColor(R.color.colorPrimary)
       }
      
      在Fragment中实现沉浸式
      在Fragment使用ImmersionBar
      • 第一种,fragment如果配合viewpager2使用的话,并且使用了Behavior指定了BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,直接在fragment的onResume里直接实现沉浸式
      • 第二种,当使用show()和hide()来控制Fragment显示隐藏的时候,直接在fragment的onResume与onHiddenChanged(参数hidden为false)方法里实现沉浸式
      • 第三种(废弃),你的Fragment直接继承SimpleImmersionFragment或者ImmersionFragment类,在initImmersionBar方法中实现沉浸式代码,只有当immersionBarEnabled返回为true才可以走initImmersionBar方法哦,不过immersionBarEnabled默认返回已经为true了,如果当前Fragment不想走沉浸式方法,请将immersionBarEnabled设置为false
      • 第四种(废弃),如果你的Fragment不能继承SimpleImmersionFragment或者ImmersionFragment类,请参考SimpleImmersionFragment实现SimpleImmersionOwner接口,或者参考ImmersionFragment实现ImmersionOwner接口
        在Activity使用ImmersionBar
        • 第一种,当结合viewpager2使用的时候,请使用viewpager2的registerOnPageChangeCallback的方法监听沉浸式
        • 第二种,当结合viewpager使用的时候,请使用viewpager的addOnPageChangeListener的方法监听沉浸式,参考demo中FragmentThreeActivity这个类
        • 第三种,当使用show()和hide()来控制Fragment显示隐藏的时候,请在tab切换的时候使用ImmersionBar,参考demo中FragmentFourActivity这个类
          使用Fragment第三方框架Fragmentation实现沉浸式
          • 参考demo中FragmentFiveActivity和BaseFiveFragment这个类
            在Dialog中实现沉浸式,具体实现参考demo
            • ①结合dialogFragment使用,可以参考demo中的BaseDialogFragment这个类

               ImmersionBar.with(this).init();
              
            • ②其他dialog,关闭dialog的时候必须调用销毁方法

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

              销毁方法:

              java中

               ImmersionBar.destroy(this, dialog);
              

              kotlin中

               destroyImmersionBar(dialog)
              
              在PopupWindow中实现沉浸式,具体实现参考demo

              重点是调用以下方法,但是此方法会导致有导航栏的手机底部布局会被导航栏覆盖,还有底部输入框无法根据软键盘弹出而弹出,具体适配请参考demo。

               popupWindow.setClippingEnabled(false);
              
              状态栏与布局顶部重叠解决方案,六种方案根据不同需求任选其一
              • ① 使用dimen自定义状态栏高度,不建议使用,因为设备状态栏高度并不是固定的

                在values-v19/dimens.xml文件下

                 25dp

                在values/dimens.xml文件下

                 0dp

                然后在布局界面添加view标签,高度指定为status_bar_height

                   
              • ② 使用系统的fitsSystemWindows属性,使用该属性不会导致输入框与软键盘冲突问题,不要再Fragment使用该属性,只适合纯色状态栏

                  

                然后使用ImmersionBar时候必须指定状态栏颜色

                 ImmersionBar.with(this)
                         .statusBarColor(R.color.colorPrimary)
                         .init();
                
                • 注意:ImmersionBar一定要在设置完布局以后使用,
                • ③ 使用ImmersionBar的fitsSystemWindows(boolean fits)方法,只适合纯色状态栏

                   ImmersionBar.with(this)
                          .fitsSystemWindows(true)  //使用该属性,必须指定状态栏颜色
                          .statusBarColor(R.color.colorPrimary)
                          .init();
                  
                • ④ 使用ImmersionBar的statusBarView(View view)方法,可以用来适配渐变色状态栏、侧滑返回

                  在标题栏的上方增加View标签,高度指定为0dp

                     

                  然后使用ImmersionBar的statusBarView方法,指定view就可以啦

                   ImmersionBar.with(this)
                             .statusBarView(view)
                             .init();
                       //或者
                       //ImmersionBar.setStatusBarView(this,view);
                  
                • ⑤ 使用ImmersionBar的titleBar(View view)方法,原理是设置paddingTop,可以用来适配渐变色状态栏、侧滑返回

                   ImmersionBar.with(this)
                                 .titleBar(view) //可以为任意view,如果是自定义xml实现标题栏的话,标题栏根节点不能为RelativeLayout或者ConstraintLayout,以及其子类
                                 .init();
                           //或者
                           //ImmersionBar.setTitleBar(this, view);
                  
                • ⑥ 使用ImmersionBar的titleBarMarginTop(View view)方法,原理是设置marginTop,只适合纯色状态栏

                   ImmersionBar.with(this)
                                 .titleBarMarginTop(view)  //可以为任意view
                                 .statusBarColor(R.color.colorPrimary)  //指定状态栏颜色,根据情况是否设置
                                 .init();
                           //或者使用静态方法设置
                           //ImmersionBar.setTitleBarMarginTop(this,view);
                  
                  解决EditText和软键盘的问题
                  • 第一种方案

                     ImmersionBar.with(this)
                                    .keyboardEnable(true)  //解决软键盘与底部输入框冲突问题
                                //  .keyboardEnable(true, WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
                                //                        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)  //软键盘自动弹出
                                    .init();
                    
                  • 第二种方案 不使用keyboardEnable方法,只需要在布局的根节点(最外层节点)加上android:fitsSystemWindows="true"属性即可,只适合纯色状态栏

                    当白色背景状态栏遇到不能改变状态栏字体为深色的设备时,解决方案
                     ImmersionBar.with(this)
                                      .statusBarDarkFont(true, 0.2f) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度
                                      .init();
                    
                    状态栏和导航栏其它方法
                    • public static boolean hasNavigationBar(Activity activity)

                      判断是否存在导航栏

                    • public static int getNavigationBarHeight(Activity activity)

                      获得导航栏的高度

                    • public static int getNavigationBarWidth(Activity activity)

                      获得导航栏的宽度

                    • public static boolean isNavigationAtBottom(Activity activity)

                      判断导航栏是否在底部

                    • public static int getStatusBarHeight(Activity activity)

                      获得状态栏的高度

                    • public static int getActionBarHeight(Activity activity)

                      获得ActionBar的高度

                    • public static boolean hasNotchScreen(Activity activity)

                      是否是刘海屏

                    • public static boolean getNotchHeight(Activity activity)

                      获得刘海屏高度

                    • public static boolean isSupportStatusBarDarkFont()

                      判断当前设备支不支持状态栏字体设置为黑色

                    • public static boolean isSupportNavigationIconDark()

                      判断当前设备支不支持导航栏图标设置为黑色

                    • public static void hideStatusBar(Window window)

                      隐藏状态栏

                      4.EdgeUtils

                      EdgeUtils:安卓沉浸式方案(edge to edge)封装

                      githup