项目需求 - 动态实现透明状态栏

关键字:透明状态栏、沉浸式、动态、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;
    }

}

四、出现的问题

项目需求 - 动态实现透明状态栏_第1张图片
预期效果

项目需求 - 动态实现透明状态栏_第2张图片
实际效果

和一般的透明状态栏不同,项目中需要的是动态改变。网上的教程大多是在 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.如何更改状态栏图标字体颜色?

你可能感兴趣的:(项目需求 - 动态实现透明状态栏)