#Android项目# ——day03 Android 沉浸式状态栏的解决方案

注明下:沉浸栏完美解决方案文章来源于: https://blog.csdn.net/u014418171/article/details/81223681
首先!!! 使用该方法之前,看了别的文章,使用了其他的方法,请先删掉你在别的文章的代码修改, 相信我, 你绝对能以最简单的方式 让你的项目实现沉浸状态栏兼容~ 包括图片沉浸!
以下代码不能出现!!!
全局搜索你的代码里是否有android:fitsSystemWindows , 删掉!, 没错 删掉!!!
检查你的values、values-v19、values-v21等 是否配置了
如下item标签

// values-v19。v19 开始有 android:windowTranslucentStatus 这个属性true true// values-v21。5.0 以上提供了 setStatusBarColor()方法设置状态栏颜色。false true@android:color/transparent

凡是在style.xml中 有关 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 统统删掉,全部删掉~
反正使用了别的沉浸栏方法删干净就对了,不然很容易出现互相干扰,各种bug
【#Android项目# ——day03 Android 沉浸式状态栏的解决方案】ok!!那接下来正式进入正题
来看看我的工程大概结构如下
#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

接下来我们准备一下工具类 1.复制一下SystemBarTintManager类到你的工程
https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java
2.创建沉浸栏工具类 StatusBarUtil
public class StatusBarUtil { public final static int TYPE_MIUI = 0; public final static int TYPE_FLYME = 1; public final static int TYPE_M = 3; //6.0@IntDef({TYPE_MIUI, TYPE_FLYME, TYPE_M}) @Retention(RetentionPolicy.SOURCE) @interface ViewType { }/** * 修改状态栏颜色,支持4.4以上版本 * * @param colorId 颜色 */ public static void setStatusBarColor(Activity activity, int colorId) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.setStatusBarColor(colorId); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //使用SystemBarTintManager,需要先将状态栏设置为透明 setTranslucentStatus(activity); SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity); systemBarTintManager.setStatusBarTintEnabled(true); //显示状态栏 systemBarTintManager.setStatusBarTintColor(colorId); //设置状态栏颜色 } }/** * 设置状态栏透明 */ @TargetApi(19) public static void setTranslucentStatus(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色 Window window = activity.getWindow(); View decorView = window.getDecorView(); //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间 int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); //导航栏颜色也可以正常设置 //window.setNavigationBarColor(Color.TRANSPARENT); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; attributes.flags |= flagTranslucentStatus; //int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; //attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); } }/** *代码实现android:fitsSystemWindows * * @param activity */ public static void setRootViewFitsSystemWindows(Activity activity, boolean fitSystemWindows) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { ViewGroup winContent = (ViewGroup) activity.findViewById(android.R.id.content); if (winContent.getChildCount() > 0) { ViewGroup rootView = (ViewGroup) winContent.getChildAt(0); if (rootView != null) { rootView.setFitsSystemWindows(fitSystemWindows); } } }}/** * 设置状态栏深色浅色切换 */ public static boolean setStatusBarDarkTheme(Activity activity, boolean dark) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { setStatusBarFontIconDark(activity, TYPE_M, dark); } else if (OSUtils.isMiui()) { setStatusBarFontIconDark(activity, TYPE_MIUI, dark); } else if (OSUtils.isFlyme()) { setStatusBarFontIconDark(activity, TYPE_FLYME, dark); } else {//其他情况 return false; }return true; } return false; }/** * 设置 状态栏深色浅色切换 */ public static boolean setStatusBarFontIconDark(Activity activity, @ViewType int type,boolean dark) { switch (type) { case TYPE_MIUI: return setMiuiUI(activity, dark); case TYPE_FLYME: return setFlymeUI(activity, dark); case TYPE_M: default: return setCommonUI(activity,dark); } }//设置6.0 状态栏深色浅色切换 public static boolean setCommonUI(Activity activity, boolean dark) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { View decorView = activity.getWindow().getDecorView(); if (decorView != null) { int vis = decorView.getSystemUiVisibility(); if (dark) { vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } else { vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } if (decorView.getSystemUiVisibility() != vis) { decorView.setSystemUiVisibility(vis); } return true; } } return false; }//设置Flyme 状态栏深色浅色切换 public static boolean setFlymeUI(Activity activity, boolean dark) { try { Window window = activity.getWindow(); 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 = https://www.it610.com/article/meizuFlags.getInt(lp); if (dark) { value |= bit; } else { value &= ~bit; } meizuFlags.setInt(lp, value); window.setAttributes(lp); return true; } catch (Exception e) { e.printStackTrace(); return false; } }//设置MIUI 状态栏深色浅色切换 public static boolean setMiuiUI(Activity activity, boolean dark) { try { Window window = activity.getWindow(); Class clazz = activity.getWindow().getClass(); @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.getDeclaredMethod("setExtraFlags", int.class, int.class); extraFlagField.setAccessible(true); if (dark) {//状态栏亮色且黑色字体 extraFlagField.invoke(window, darkModeFlag, darkModeFlag); } else { extraFlagField.invoke(window, 0, darkModeFlag); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } //获取状态栏高度 public static int getStatusBarHeight(Context context) { int result = 0; int resourceId = context.getResources().getIdentifier( "status_bar_height", "dimen", "android"); if (resourceId > 0) { result = context.getResources().getDimensionPixelSize(resourceId); } return result; } }

3.创建沉Rom类型判断的工具类 OSUtils
public class OSUtils {public static final String ROM_MIUI = "MIUI"; public static final String ROM_EMUI = "EMUI"; public static final String ROM_FLYME = "FLYME"; public static final String ROM_OPPO = "OPPO"; public static final String ROM_SMARTISAN = "SMARTISAN"; public static final String ROM_VIVO = "VIVO"; public static final String ROM_QIKU = "QIKU"; private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; private static String sName; private static String sVersion; public static boolean isEmui() { return check(ROM_EMUI); }public static boolean isMiui() { return check(ROM_MIUI); }public static boolean isVivo() { return check(ROM_VIVO); }public static boolean isOppo() { return check(ROM_OPPO); }public static boolean isFlyme() { return check(ROM_FLYME); }public static boolean is360() { return check(ROM_QIKU) || check("360"); }public static boolean isSmartisan() { return check(ROM_SMARTISAN); }public static String getName() { if (sName == null) { check(""); } return sName; }public static String getVersion() { if (sVersion == null) { check(""); } return sVersion; }public static boolean check(String rom) { if (sName != null) { return sName.equals(rom); }if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) { sName = ROM_MIUI; } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) { sName = ROM_EMUI; } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) { sName = ROM_OPPO; } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) { sName = ROM_VIVO; } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) { sName = ROM_SMARTISAN; } else { sVersion = Build.DISPLAY; if (sVersion.toUpperCase().contains(ROM_FLYME)) { sName = ROM_FLYME; } else { sVersion = Build.UNKNOWN; sName = Build.MANUFACTURER.toUpperCase(); } } return sName.equals(rom); }public static String getProp(String name) { String line = null; BufferedReader input = null; try { Process p = Runtime.getRuntime().exec("getprop " + name); input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); line = input.readLine(); input.close(); } catch (IOException ex) { return null; } finally { if (input != null) { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } return line; } }

4.开始适配
首先在Activity 的setContentView 下一行编写如下代码(一般你可以写到你的BaseActivity里,否则你每个activity都得写一次)
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.xxx); //绑定布局//这里注意下 因为在评论区发现有网友调用setRootViewFitsSystemWindows 里面 winContent.getChildCount()=0 导致代码无法继续 //是因为你需要在setContentView之后才可以调用 setRootViewFitsSystemWindows //当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding StatusBarUtil.setRootViewFitsSystemWindows(this,true); //设置状态栏透明 StatusBarUtil.setTranslucentStatus(this); //一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清 //所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容 if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) {//true状态栏为黑色文字和图标 //如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明, //这样半透明+白=灰, 状态栏的文字能看得清 StatusBarUtil.setStatusBarColor(this,0x55000000); } }

以下是Fragment的写法,同样也是建议你的BaseFragment里,否则你每个Fragment都得写一次
@Override public void onStart() { super.onStart(); //这里注意下 因为在评论区发现有网友调用setRootViewFitsSystemWindows 里面 winContent.getChildCount()=0 导致代码无法继续 //是因为你需要在setContentView之后才可以调用 setRootViewFitsSystemWindows//当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding StatusBarUtil.setRootViewFitsSystemWindows(getActivity(), true); //设置状态栏透明 StatusBarUtil.setTranslucentStatus(getActivity()); //一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清 //所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容 if (!StatusBarUtil.setStatusBarDarkTheme(getActivity(), true)) { //如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明, //这样半透明+白=灰, 状态栏的文字能看得清 StatusBarUtil.setStatusBarColor(getActivity(), 0x55000000); } }

上面先这样 由于界面风格很多, 比如同一个app有 的界面是黑色风格的页面, 有的是白色风格的页面,有的是顶部是图片的界面希望沉浸进去 这样更好看, 同时 此时状态栏文字要跟随改变
比如我这个 4个不同的fragment,有一个是白色, 另外两个是顶部是图片的
我是这样切换状态栏文字深浅色的,你们参考下
界面设置状态栏黑色图标
#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

界面设置状态栏白色图标
#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

改变的代码如下
#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

如果就这样草草结束的话肯定不是我的style了,重点来了!
我要把图片也沉浸进去!!! 想要图片沉浸, 必须设置fitsSystemWindows=false, 以去掉padding效果, 然后想办法 把图片上层的 其他View 整体 paddingTop=状态栏高 让其他View向下挪动
setRootViewFitsSystemWindows(this,false);
去掉padding效果后 图片沉浸了! 但内容进入了状态栏里 被遮挡.
那以最方便的方式就是让整个内容布局 往下挪动
自定义一个View ,用来做状态栏高度占位
StatusBarHeightView 代码如下
/** * 作者:东芝 * 功能:状态栏高度View,用于沉浸占位 */public class StatusBarHeightView extends LinearLayout { private int statusBarHeight; private int type; public StatusBarHeightView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(attrs); }public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); }public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); init(attrs); }private void init(@Nullable AttributeSet attrs) {int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if(resourceId>0) { statusBarHeight = getResources().getDimensionPixelSize(resourceId); } }else{ //低版本 直接设置0 statusBarHeight = 0; } if (attrs != null) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.StatusBarHeightView); type = typedArray.getInt(R.styleable.StatusBarHeightView_use_type, 0); typedArray.recycle(); } if (type == 1) { setPadding(getPaddingLeft(), statusBarHeight, getPaddingRight(), getPaddingBottom()); } }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (type == 0) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), statusBarHeight); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }

attrs.xml

代码很简单, 就是写一个View, 支持paddingTop= 状态栏高度值 的View,
解释下两个类型:
use_height: 设置当前布局高度=状态栏高度值 用于无子View时的占位
use_padding_top: 设置当前顶部padding=状态栏高度值 用于有子View时的占位
适配低于4.4时 占位View的高度为0 所以不可见
使用方法, 用StatusBarHeightView 来包住你要往下移动的内容! 单独留出要沉浸的View不包住,
举例:

#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

效果如下:
#Android项目# ——day03 Android 沉浸式状态栏的解决方案
文章图片

End 如果想详细了解请看原创大佬的文章:Android 沉浸式状态栏完美解决方案

    推荐阅读