大家好,欢迎来到IT知识分享网。
国内很多类似的文章, 我只想说一个字, 真tm乱! 我看不懂… 评论里面 全在说无效什么的 (我试了也无效, 好厉害的样子)
不废话,回到正题, 首先贴上一个众所周知的库 SystemBarTint
我只要这个类
https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java
然后复制到你的工程
这个类我就不多说了, 就是兼容4.x以上沉浸透明状态栏的 一个兼容类
1.开始
先贴工具类, 有部分代码参考自网上并有做改动,
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 = 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;
}
}
好了,这个类 支持了 设置状态栏透明, 设置状态栏颜色, 支持了状态栏深色浅色切换(则状态栏上的文字图标颜色)
怕搞了一堆在别的文章的配置,所以我还是要说下以下代码不能出现:
全局搜索你的代码里 是否有 android:fitsSystemWindows , 删掉!
检查你的values、values-v19、values-v21等 是否配置了
如下item标签
// values-v19。v19 开始有 android:windowTranslucentStatus 这个属性
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowTranslucentNavigation">true</item> </style>
// values-v21。5.0 以上提供了 setStatusBarColor() 方法设置状态栏颜色。
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowTranslucentStatus">false</item> <item name="android:windowTranslucentNavigation">true</item> <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色--> <item name="android:statusBarColor">@android:color/transparent</item> </style>
凡是在style.xml中 有关 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 统统删掉
因为 我们要通过代码去实现, xml中的各种属性全部不要写, 避免代码出现互相干扰, 出现各种 无效的bug
好了干扰已全部去除,开始适配
首先在Activity 的setContentView 下一行编写如下代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
//设置状态栏透明
StatusBarUtil.setTranslucentStatus(this);
//一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清
//所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容
if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) {
//如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明,
//这样半透明+白=灰, 状态栏的文字能看得清
StatusBarUtil.setStatusBarColor(this,0x55000000);
}
}
2.重新认识fitsSystemWindows
有没有遇到过一个问题: 写一个布局 想要背景图片沉浸进去, 但是我又想要图片沉浸进去但内容如何控制不要沉浸进去?
一开始我搞笑的弄了个自定义View 里面内置了paddingTop=状态栏高度 的功能, 效果挺好, 后来发现其实官方已经帮我们做好了 只是我们用不好fitSystemWindows而已… 现在跟我分析分析fitSystemWindows的原理
为了理解这个玩意的作用, 我看了下源码, 原来fitsSystemWindows=true可以理解为 paddingTop=状态栏高度, =false可以理解为沉浸进状态栏
当然你直接设置肯定没效果 你还要设置我上面说的StatusBarUtil.setTranslucentStatus(this);, 其实关键代码就是
window.statusBarColor = Color.TRANSPARENT
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
把窗口全屏而已
现在来说说他们直接是如何传递的, fitsSystemWindows在ViewGroup中通过dispatchApplyWindowInsets进行分发给子View
如果dispatchApplyWindowInsets 中把insets.consumeSystemWindowInsets()消费掉, 那么inset事件就无法传递到子View,子View设置fitsSystemWindows=true 将会没有反应,View会通过onApplyWindowInsets消费掉WindowInsets, 当然其要求是父View必须设置fitSystemWindows=false 这样WindowInsets才能传递到子View中进行消费
那么, fitSystemWindows是如何实现paddingTop效果的呢?
源码发现
computeFitSystemWindows 方法把系统的状态栏高度, 导航栏高度等计算 最终放到localInsets 这个rect中, 此时的rect.top就是状态栏高度
然后通过applyInsets->internalSetPadding, 把mPaddingTop=localInsets.top 完成状态栏高度的padding
至此 简单的分析了fitSystemWindows的作用
那么我们具体如何控制呢, 其实很简单 我们先来看一个错误的示范
如图, 很多时候我们的既有认知认为是设置为子View就有效果…
其实…前面看源码说了 ,他是对子View再加paddingTop=状态栏高度
所以正确的做法是再包裹一层
完事. 这样你就可以随心所欲控制哪个View 要沉浸 哪个不沉浸
当然如果需要动态控制的话 可以重写父View dispatchApplyWindowInsets 进行分发控制
================================================================
================================================================
补充:
================================================================
================================================================
值得注意的是 如果你按我那样去做, 状态栏颜色无法被修改, 请检查上层布局是否设置了背景
或者受了全局主题的
<item name="android:windowBackground">@color/xxx</item>
的颜色影响
感谢@Narbolo 的提醒, 漏了个Rom类型判断的工具类,现在贴上
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;
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/26431.html