朝圣言
扫描关注朝圣言

手机扫描二维码

cordova与css结合实现在android上透明状态栏兼容齐刘海等异形屏

朝圣言2019-06-27前端仕锦 2605 0A+A-

应该会有人觉得百度(google)有的东西为什么我会写出来?有我还写啥啊~

手上有一个项目,是用cordova+vue的,这个不是重点,由于ios的app上架比较困难,所以优先上架android。

因为习惯了在ios上透明状态栏的样式,在android上也想拥有。得赖于ios的css中safe-area-inset-*的属性,可以在cordova的ios端结合cordova-plugin-statusbar插件实现透明状态栏,并且在顶部(或者底部,根据需要),让出安全距离,避免被顶部状态栏文字和底部横线挡住,于是心想在安卓也可以吧~

可以我就不写这篇了!!!!

预期效果如图

cordova与css结合实现在android上透明状态栏兼容齐刘海等异形屏  前端 web js cordova app 第1张

首先,贸然使用safe-area-inset-*在安卓上会出现2大问题!

1、没效果!

cordova-plugin-statusbar插件已经可以使得状态栏变得透明,且根据需要变换字体颜色?然而本该出现安全距离却没有也就是默认0?

值得注意的是,在我大量测试后发现,单独通过一句透明化状态栏的代码去实现是不可以的,我不知道是不是我的用法不对,还是确实是这样。这里我用了3句代码才实现了透明化。放代码!!

StatusBar.show();
StatusBar.overlaysWebView(false);
StatusBar.overlaysWebView(true);

经过查看相关文档发现,该属性是ios独有的。

2、不兼容部分内核导致样式出错

没效果是最好的情况,我在同事的uc浏览器,无论手机版还是pc版都出现了由于不兼容safe-area-inset-*属性导致整个样式无法被渲染,出现了验证的样式bug,其实本该默认这个值为0的,却在部分内核下无法被识别。

推测,应该是使用了较低版本的内核导致的问题,所以出现了这个问题。

综上:解决问题迫在眉睫;

我们先解决第二个问题;

因为在本项目css中使用大量的calc方法,大胆推测var方法定义是没问题的。小范围实现证实想法!

:root是根结点也就是html,我们需要在里面重新定义safe-area-inset-*值!

值得一提的是,ios11于ios12使用safe-area-inset-*的方法是不一样的

ios11及以下使用constant(safe-area-inset-*)

ios12使用env(safe-area-inset-*)

在考虑用户群体可能使用ios11的情况下,必须统一,于是css兼容代码就诞生了

:root {
  --safe-area-inset-top: 0px;
  --safe-area-inset-right: 0px;
  --safe-area-inset-bottom: 0px;
  --safe-area-inset-left: 0px;
  @supports (top: constant(safe-area-inset-top)) {
    --safe-area-inset-top: constant(safe-area-inset-top);
    --safe-area-inset-right: constant(safe-area-inset-right);
    --safe-area-inset-bottom: constant(safe-area-inset-bottom);
    --safe-area-inset-left: constant(safe-area-inset-left);
  }
  @supports (top: env(safe-area-inset-top)) {
    --safe-area-inset-top: env(safe-area-inset-top);
    --safe-area-inset-right: env(safe-area-inset-right);
    --safe-area-inset-bottom: env(safe-area-inset-bottom);
    --safe-area-inset-left: env(safe-area-inset-left);
  }
}


通过supports探针我们对把支持safe-area-inset-*赋予--safe-area-inset-*,对不支持的,一律赋值0px!!!

谨记,这里不能写0,必须是完整的0px;否则可能无效

最后,在凡是用到safe-area-inset-*的地方,改写为var(--safe-area-inset-*)

例如

div{
    top:calc(44px - var(--safe-area-inset-top));
}

解决第二个问题了

回头我们攻克第一个问题

其实第二个问题网上有,只是没这么详细,第一个问题,也不必强求解决,但是对于喜欢研究的程序员来说,是一个研究的机会。

要解决第一个问题,必须基于第二个问题的基础上。

我们用的cordova-plugin-statusbar插件是不支持获取状态栏高度的,这就是为什么需要用css让出安全距离的一个原因。

假如,我们可以通过js获取到状态栏高度,利用第二个问题的css,动态拼接初始值动态写入html中,由于vue是单页应用,所以只要要写入一次,对所有的页面都必定是有效的。

要想获得状态栏高度,必须改写安卓代码,方法有2个

1、我用的是腾讯的X5内核,去改写其webview代码,在获取状态栏高度后,通过css注入到webview中!!研究了一个早上太过于硬核,放弃!

2、修改cordova-plugin-statusbar,使得我们可以用js去获取状态栏高度

放出代码,语法就不说了,需要会写安卓的人,我也是各自尝试再写出来的!

找到这个java文件,并写入代码

cordova与css结合实现在android上透明状态栏兼容齐刘海等异形屏  前端 web js cordova app 第2张

if ("getStatusBarHeight".equals(action)) {
            int result = 0;
            try {
                DisplayMetrics dm = new DisplayMetrics();
                dm = cordova.getActivity().getResources().getDisplayMetrics();
                float density = dm.density;
                int resourceId = cordova.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
                if (resourceId > 0) {
                    result = cordova.getActivity().getResources().getDimensionPixelSize(resourceId);
                }
                Log.i("getStatusBarHeight", String.valueOf(result / density));
                callbackContext.success(String.valueOf((result / density)));
            } catch (Exception e) {
                callbackContext.error("error");
            }
        }

调试输出的语句大可去掉就好。找到对应的js文件

cordova与css结合实现在android上透明状态栏兼容齐刘海等异形屏  前端 web js cordova app 第3张

加入

getStatusBarHeight:function(success, error){
  exec(success, error,'StatusBar','getStatusBarHeight',[])
},


在vue入口文件main.ts(或者main.js)中的监听deviceReady中加入ts(js)代码

StatusBar.getStatusBarHeight((height: string) => {
            if (Number(height) > 0) {
                //一些错误规避
                //StatusBar.show();
                //StatusBar.overlaysWebView(false);
                //StatusBar.overlaysWebView(true);
                const code: string = `:root{--safe-area-inset-top:${height}px;--safe-area-inset-right:0px;--safe-area-inset-bottom:0px;--safe-area-inset-left:0px;` +
                    `@supports(top:constant(safe-area-inset-top)){--safe-area-inset-top:constant(safe-area-inset-top);--safe-area-inset-right:constant(safe-area-inset-right);` +
                    `--safe-area-inset-bottom:constant(safe-area-inset-bottom);--safe-area-inset-left:constant(safe-area-inset-left)}` +
                    `@supports(top:env(safe-area-inset-top)){--safe-area-inset-top:env(safe-area-inset-top);--safe-area-inset-right:env(safe-area-inset-right);` +
                    `--safe-area-inset-bottom:env(safe-area-inset-bottom);--safe-area-inset-left:env(safe-area-inset-left)}}`;
                const style: any = document.createElement('style');
                style.type = 'text/css';
                style.rel = 'stylesheet';
                style.appendChild(document.createTextNode(code));
                const head: any = document.getElementsByTagName('head')[0];
                head.appendChild(style);
                StatusBar.styleDefault();
            } else {
                StatusBar.show();
            }

然后编译,测试,微调,完美解决!!

文章关键词
前端
web
js
cordova
app
发表评论