关键字:透明状态栏、沉浸式、动态、WebView
一、需求
现在所有的 web 页面都是 titlebar + webview 实现的,但是新的页面需要全透明的全屏样式,逻辑比较多,为了和 ios 统一,必须复用之前的页面,因此,需要实现动态地修改页面的样式。
二、起步
网络上关于透明状态栏、沉浸式体验的文章已经很多了,简单调试了一下。
三、一个工具类
/**
* Created by Arno on 2017/9/15.
*/
public class StatusBarCompat {
private static final String TAG = "StatusBarCompat";
private static int sDefalutColor = Color.parseColor("#000000");
/**
* 设置透明状态栏
* @param activity
*/
public static void compatTranslucentBar(Activity activity) {
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
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.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
/**
* 设置透明状态栏,并设置状态栏颜色
* @param activity
* @param statusColor 设置状态栏颜色
*/
public static void compatStatusBarColor(Activity activity,int statusColor) {
Window window = activity.getWindow();
int color = statusColor == -1 ? sDefalutColor : statusColor;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
window.setStatusBarColor(color);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
DensityUtils.dip2px(activity, 25));
statusBarView.setBackgroundColor(color);
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
contentView.addView(statusBarView, lp);
}
}
/**
* 已知系统类型时,设置状态栏黑色文字、图标。
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
* @param activity
*/
public static void StatusBarLightMode(Activity activity){
switch (Build.MANUFACTURER) {
case "Xiaomi":
MIUISetStatusBarLightMode(activity, true);
break;
case "Meizu":
FlymeSetStatusBarLightMode(activity.getWindow(), true);
break;
default:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
break;
}
}
/**
* 状态栏暗色模式,清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
* @param activity
*/
public static void StatusBarDarkMode(Activity activity){
switch (Build.MANUFACTURER) {
case "Xiaomi":
MIUISetStatusBarLightMode(activity, false);
break;
case "Meizu":
FlymeSetStatusBarLightMode(activity.getWindow(), false);
break;
default:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_VISIBLE);
}
break;
}
}
/**
* 设置状态栏图标为深色和魅族特定的文字风格
* 可以用来判断是否为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;
}
/**
* 需要MIUIV6以上
* @param activity
* @param dark 是否把状态栏文字及图标颜色设置为深色
* @return boolean 成功执行返回true
*
*/
public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
boolean result = false;
Window window=activity.getWindow();
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
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;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
if(dark){
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}catch (Exception e){
}
}
return result;
}
}
四、出现的问题
和一般的透明状态栏不同,项目中需要的是动态改变。网上的教程大多是在 Activity 刚刚创建的时候设置了属性,使得界面呈透明效果,但是现在需要在界面展示完成后,在事件中,完成改变。
我是这样做的:
public void onClick(View v){
if (Build.VERSION_CODES.KITKAT>Build.VERSION.SDK_INT ) {
return;
}
handler.sendEmptyMessage(0);
}
private Handler = new Handler(Looper.getMainLooper()){
@override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
StatusBarCompat.compatTranslucentBar(WebViewActivity.this);
mContainer.requestLayout();//mContainer 为页面的根布局
break;
}
}
};
但是上面的代码呈现的效果是,页面透明后,整个布局上移,下面空余出了一个状态栏高度的空白,像是 requestLayout() 并没有起作用。
说出来你可能不信,我最后还是解决了。
在一段时间的尝试后,发现,把 handler.sendEmptyMessage(0) 改为 handler.postDelay(runnable, 0) 可以刷新,虽然 handler.post(runnable) 和 postDelay 的逻辑相同,但是貌似只有用 postDelay 才生效。
惭愧又尴尬。
我突然又想起来一件事情,通常向 handler 发了一个消息之后不会只执行
mContainer.requestLayout(); 这个代码。项目中还出现过这样一个问题:
private Handler = new Handler(Looper.getMainLooper()){
@override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
// do some UI job
StatusBarCompat.compatTranslucentBar(WebViewActivity.this);
mContainer.requestLayout();//mContainer 为页面的根布局
break;
}
}
};
上面这种情况,UI 依然只是上移而不会充满整个屏幕需要在做完其他操作后,另外发一个消息,进行 requestLayout 操作
private Handler = new Handler(Looper.getMainLooper()){
@override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
// do some UI job
StatusBarCompat.compatTranslucentBar(WebViewActivity.this);
sendEmptyMessage(1);
break;
case 1:
mContainer.requestLayout();//mContainer 为页面的根布局
break;
}
}
};
参考:
1.如何判断设备系统?
2.如何更改状态栏图标字体颜色?