请选择 进入手机版 | 继续访问电脑版

从0到1优雅实现沉浸式状态栏

[复制链接]
阿峻 发表于 2021-1-1 18:32:46 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
  

   /   本日科技快讯   /  

   近日,国家计算机病毒中心发布了《移动APP违法违规问题及治理办法》。此中,MOMO陌陌、本日头条、京东金融、云闪付等下载量很高的应用也名列此中。   
    2016年至今,国家计算机病毒中心针对国表里的400多个应用发布渠道,共爬取移动应用APP总数量694万款,安全检测APP30515款,检测出恶意样本8000余款。  

   /   作者简介   /  

   本篇文章来自Jimmy-Sun的投稿,分享了实现沉醉式状态栏的方方面面,相信会对各人有所资助!同时也感谢作者贡献的精彩文章。  

   Jimmy-Sun的博客地点:  
     https://me.csdn.net/u013541140   


   /   概述   /  

   现在市面上绝大多数APP都实现了沉醉式状态栏的效果,该效果可以极大地提升用户的使用体验。但是,APP默认的情况下只是设定一个固定的状态栏颜色,那么就需要我们通过代码去动态修改状态栏颜色的。那么,如何去实现呢?  

   首先来说,从Android 4.4开始才气实现沉醉式状态栏的,所以如果您的APP也支持Android 4.4以下版本,那还需要对Android 4.4以下版本做“不支持沉醉式状态栏”处理。  

   那么,从Android 4.4开始,大概可以分成三个阶段来实现沉醉式状态栏:  

   Android4.4(API 19) - Android 5.0(API 21):这个阶段的实现方式为:通过FLAG_TRANSLUCENT_STATUS设置状态栏为透明并且为全屏模式,然后通过添加一个与StatusBar一样巨细的View,将View的配景设置为要设置的颜色,从而实现沉醉式。   
    Android 5.0(API 21) - Android 6.0(API 23):从Android 5.0开始,参加了一个重要的属性android:statusBarColor和方法setStatusBarColor(),通过这个方法我们就可以轻松实现沉醉式状态栏。但是在Android 6.0以下版本官方不支持设置状态栏的文字和图标颜色,现在只有小米和魅族的ROM提供了支持。   
    Android 6.0(API 23)以上版本:实在Android 6.0以上的实现方式和Android 5.0+ 是一样的,区别是从Android 6.0开始,官方支持改变状态栏的文字和图标的颜色。  

   /   实现方案   /  

   为了实现沉醉式状态栏的效果,我们需要做一些前期工作,如下:   
    修改应用主题   
    为了更好地演示沉醉式状态栏的效果,我们修改应用的主题,使其不要显示Android默认的标题栏。  打开styles.xml文件,可以改成如下代码,并且将colorPrimary等颜色设置删掉:   
   
  1. [/code]     
  2.       
  3.   [size=4]获取状态栏高度[/size]
  4.    
  5.     新建一个StatusBarUtils.java文件,在内里添加一个获取状态栏高度的方法,以后我们都会用的到这个方法。之后我们的焦点代码也在这个工具类里实现。代码如下:   
  6.     [code]
复制代码
    public class StatusBarUtils {
    public static int getHeight(Context context) {
        int statusBarHeight = 0;
        try {
            int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
                    "android");
            if (resourceId > 0) {
                statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusBarHeight;
    }

}
      
    下面我们就按照从高版本到低版本的顺序来讲授如何更优雅地实现沉醉式状态栏。   
    /   Android 5.0+   /  

设置状态栏颜色

   
    在StatusBarUtils类里添加如下方法,实现设置状态栏颜色的功能:   
   
  1. [/code]     public static void setColor(@NonNull Window window, @ColorInt int color) {
  2.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  3.         window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  4.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  5.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  6.         window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
  7.         window.setStatusBarColor(color);
  8.     }
  9. }
  10.       
  11.     修改状态栏颜色的功能实在就是对Window举行操作,而该Window可以是Activity或Dialog等持有的Window,所以我们就封装了一个传递Window的方法。  为了便于对Activity直接操作,可以再增加一个如下方法:   
  12.     [code]
复制代码
    public static void setColor(Context context, @ColorInt int color) {
    if (context instanceof Activity) {
        setColor(((Activity) context).getWindow(), color);
    }

}
      
    下面测试一下,新建一个Activity,将其布局配景设置为某个颜色,例如主题色,布局代码如下:   
   
  1. [/code]     
  2.       
  3.     在没设置状态栏颜色时,展示效果如下:   
  4.     [align=center][img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/v1LbPPWiaSt5EHTLkHWMfgfIKvXicBibOZJ7VMafqoeHkyaTg5IDSs5T7BYAnBeoUDwUdC5cWnlQwYJkEiccvgxBzg/640?wx_fmt=png[/img][/align]  
  5.        接下来在Activity的onCreate()方法里调用设置状态栏颜色的方法,代码如下:  
  6.    [code]
复制代码
    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_color);
    StatusBarUtils.setColor(this, getResources().getColor(R.color.colorPrimaryDark));

}
      
    展示效果如下:  

   
  

   这样状态栏和内容的颜色就可以一样啦!  

设置状态栏文字颜色

   
    下面我们把配景和状态栏颜色改成纯绿色(#00FF00),看看效果:   
   
  

       发现颜色好亮啊,状态栏里的文字都看不清了。在这种情况下,我们是可以将状态栏文字的颜色改成深色的,官方也仅支持设置状态栏文字和图标的深色模式和浅色模式,但是官方仅在Android 6.0以上版本提供支持。设置代码如下:   
   
  1. [/code]     private static void setTextDark(Window window, boolean isDark) {
  2.     if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
  3.         View decorView = window.getDecorView();
  4.         int systemUiVisibility = decorView.getSystemUiVisibility();
  5.         if (isDark) {
  6.             decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  7.         } else {
  8.             decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  9.         }
  10.     }
  11. }
  12.       
  13.     同样再增加一个对Activity的支持:  
  14.    [code]
复制代码
    public static void setTextDark(Context context, boolean isDark) {
    if (context instanceof Activity) {
        setTextDark(((Activity) context).getWindow(), isDark);
    }
}
   

   为了可以或许根据状态栏配景颜色的深浅而自动设置文字的颜色,我们再新增一个判断颜色深浅的方法:  

  
  1. [/code]     public static boolean isDarkColor(@ColorInt int color) {
  2.     return ColorUtils.calculateLuminance(color) = Build.VERSION_CODES.LOLLIPOP) {
  3.         window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  4.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  5.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  6.         window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
  7.         window.setStatusBarColor(color);
  8.         setTextDark(window, !isDarkColor(color));
  9.     }
  10. }
  11.    
  12.    运行一下,就可以看到效果了:  
  13.    [align=center][img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/v1LbPPWiaSt5EHTLkHWMfgfIKvXicBibOZJoWiandibbBamicPHzHWh96w9DHq0mgys6Co0w1XVEjfe4AdIUhrDgIsqw/640?wx_fmt=png[/img][/align]  
  14.    上面是Android 6.0以上版本的实现,那么对于Android 6.0以下的手机怎么办呢?现在Android 5.0-6.0的手机只有小米MIUI和魅族Flyme系统提供了支持。小米MIUI的设置方法如下:   
  15.     [code]
复制代码
    private static void setMIUIDark(Window window, boolean isDark) {
    try {
        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);
        extraFlagField.invoke(window, isDark ? darkModeFlag : 0, darkModeFlag);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
      
    魅族Flyme的设置方法如下:  

  
  1. [/code]     private static void setFlymeDark(Window window, boolean isDark) {
  2.     if (window != null) {
  3.         try {
  4.             WindowManager.LayoutParams lp = window.getAttributes();
  5.             Field darkFlag = WindowManager.LayoutParams.class
  6.                     .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
  7.             Field meizuFlags = WindowManager.LayoutParams.class
  8.                     .getDeclaredField("meizuFlags");
  9.             darkFlag.setAccessible(true);
  10.             meizuFlags.setAccessible(true);
  11.             int bit = darkFlag.getInt(null);
  12.             int value = meizuFlags.getInt(lp);
  13.             if (isDark) {
  14.                 value |= bit;
  15.             } else {
  16.                 value &= ~bit;
  17.             }
  18.             meizuFlags.setInt(lp, value);
  19.             window.setAttributes(lp);
  20.         } catch (Exception e) {
  21.             e.printStackTrace();
  22.         }
  23.     }
  24. }
  25.       
  26.     然后在setTextDark()方法中添加如下代码:  
  27.    [code]
复制代码
    private static void setTextDark(Window window, boolean isDark) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        View decorView = window.getDecorView();
        int systemUiVisibility = decorView.getSystemUiVisibility();
        if (isDark) {
            decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        } else {
            decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        switch (OSUtils.getRomType()) {
            case MIUI:
                setMIUIDark(window, isDark);
                break;
            case Flyme:
                setFlymeDark(window, isDark);
                break;
            default:
        }
    }
}
   

   Android 6.0以下运行效果同上,我就不给各人截图了。   
  设置状态栏透明

   
    当我们APP的配景是一张图片时,未设置沉醉式状态栏的效果如下:   
   
  

       这时一般我们需要将图片顶到状态栏里,也就是整个内容布局顶到状态栏里,并设置状态栏的颜色透明,才气实现沉醉式状态栏的效果。   
    那么,在我们的StatusBarUtils类里添加如下代码:   
   
  1. [/code]     public static void setTransparent(@NonNull Window window) {
  2.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  3.         window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  4.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  5.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  6.         window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
  7.         window.setStatusBarColor(Color.TRANSPARENT);
  8.     }
  9. }
  10.       
  11.     同样针对Activity,增加如下方法:  
  12.    [code]
复制代码
    public static void setTransparent(Context context) {
    if (context instanceof Activity) {
        setTransparent(((Activity) context).getWindow());
    }
}
      
    最后在Activity的onCreate()方法里设置一下:   
   
  1. [/code]     @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3.     super.onCreate(savedInstanceState);
  4.     setContentView(R.layout.activity_transparent);
  5.     StatusBarUtils.setTransparent(this);
  6. }
  7.       
  8.     运行,显示效果如下:   
  9.     [align=center][img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_jpg/v1LbPPWiaSt5EHTLkHWMfgfIKvXicBibOZJv3ZMQYOukDt1qFicjkZ2ic4YwthYugSQV2pk2UzrnibTY2BgAzIjS01NA/640?wx_fmt=jpeg[/img][/align]   
  10.     针对这种情况,各人在实现布局时不要忘了把状态栏的高度也思量进去。  以上就是Android 5.0以上沉醉式状态栏的实现了。   
  11.      /   Android 4.4+   /  
  12.    下面针对Android 4.4-5.0的手机举行实现。实现原理是将内容布局设为全屏,然后在布局的顶部添加一个和状态栏一样高度的View,将该View的配景设置成我们想要的颜色。当需要将状态栏设置纯颜色时,为了和Android 5.0以上版本保持一致,我们对内容布局的上边设置一个padding,巨细为状态栏的高度。  
  13.    为了能复用这个View,我们新增一个自界说的ID,在values文件夹下新建ids.xml文件,新增代码如下:   
  14.     [code]
复制代码
   

    

      
    然后在StatusBarUtils类里添加如下代码:   
   
  1. [/code]     private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.fake_status_bar_view;
  2. @RequiresApi(api = Build.VERSION_CODES.KITKAT)
  3. public static void setColor(@NonNull Window window, @ColorInt int color,
  4.                             boolean isTransparent) {
  5.     Context context = window.getContext();
  6.     window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  7.     window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  8.     ViewGroup decorView = (ViewGroup) window.getDecorView();
  9.     View contentView = decorView.findViewById(android.R.id.content);
  10.     if (contentView != null) {
  11.         contentView.setPadding(0, isTransparent ? 0 : getHeight(context), 0, 0);
  12.     }
  13.     View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
  14.     if (fakeStatusBarView != null) {
  15.         fakeStatusBarView.setBackgroundColor(color);
  16.         if (fakeStatusBarView.getVisibility() == View.GONE) {
  17.             fakeStatusBarView.setVisibility(View.VISIBLE);
  18.         }
  19.     } else {
  20.         // 绘制一个和状态栏一样高的矩形
  21.         View statusBarView = new View(context);
  22.         FrameLayout.LayoutParams layoutParams =
  23.                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  24.                         getHeight(context));
  25.         statusBarView.setLayoutParams(layoutParams);
  26.         statusBarView.setBackgroundColor(color);
  27.         statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
  28.         decorView.addView(statusBarView);
  29.     }
  30. }
  31.       
  32.     设置状态栏颜色  
  33.    在设置纯颜色时,我们还需要将该颜色与玄色举行1:1的肴杂。为什么要这么设置呢?因为状态栏的文字和图标颜色默认是白色的,并且在Android 5.0以下是不能修改的,所以如果修改成较浅的颜色,就会导致状态栏文字看不清的现象,因此做一个比力暗的浮层效果更好一些。  
  34.    那么将setColor()方法改成如下代码:   
  35.     [code]
复制代码
    public static void setColor(@NonNull Window window, @ColorInt int color) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.setStatusBarColor(color);
        setTextDark(window, !isDarkColor(color));
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        setColor(window, ColorUtils.blendARGB(Color.TRANSPARENT, color, 0.5f), false);
    }
}
      
    展示效果如下:   
   
  

     设置状态栏透明

   
    在设置状态栏透明时,为了也能清楚地看清状态栏的文字,我们直接设置状态栏的颜色为50%透明度的玄色。   
    于是,修改setTransparent()方法如下:  

  
  1. [/code]     public static void setTransparent(@NonNull Window window) {
  2.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  3.         window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  4.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  5.         window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  6.         window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
  7.         window.setStatusBarColor(Color.TRANSPARENT);
  8.     } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  9.         setColor(window, 0x80000000, true);
  10.     }
  11. }
  12.       
  13.     展示效果如下:   
  14.     [align=center][img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/v1LbPPWiaSt5EHTLkHWMfgfIKvXicBibOZJqicH4leyerSx8eUyH41EJeYTdriaZjkUPKfevhoygicL6CAXHqBX0jF9A/640?wx_fmt=png[/img][/align]  
  15.        这样,Android 4.4以上的手机就适配完毕了。  
  16.    /   特殊场景   /  
  17.    现在来针对一些特殊场景举行适配,比如启动页、刘海屏和弹窗等等。  
  18. [size=5]启动页[/size]
  19.    
  20.     对于启动页,一般都会把状态栏收上去,这需要适配刘海屏,否则刘海区域会显示黑的一片。实在Android P以上提供了适配刘海屏的方法,在启动页Activity添加如下代码:   
  21.     [code]
复制代码
    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    StatusBarUtils.setTransparent(this);
    // 适配刘海屏
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.layoutInDisplayCutoutMode =
                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        getWindow().setAttributes(layoutParams);
    }
}
      
    但是一些手机厂商的刘海屏手机系统版本是低于Android P的,不外也都提供了适配的方法。适配方式是在AndroidManifest.xml文件里的application标签下添加如下代码:   
   
  1. [/code]     
  2.    
  3.    别的,对于Android 5.0以下的手机,适配完刘海屏后会在顶部多一块玄色半透明的View,那我们将其改成全透明的,修改onCreate()方法如下:   
  4.     [code]
复制代码
    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    StatusBarUtils.setTransparent(this);
    // 适配刘海屏
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.layoutInDisplayCutoutMode =
                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        getWindow().setAttributes(layoutParams);
    }
    // 适配Android 4.4
    if (Build.VERSION.SDK_INT 
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题

专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )