Cordova&ionic android 状态栏和底部导航栏适配问题

在APP使用过程中(尤其是在华为、vivo等国产手机上),会出现以下适配问题:

  1. 底部导航栏遮挡住footerBar或其他内容
  2. 状态栏遮挡住header等内容
  3. 初始化时正常,弹出键盘或从后台再切换回来时再次出现1问题并不可还原
  4. 彻底隐藏底部导航栏时,部分手机无法唤起底部导航栏,又不支持上滑或左滑来切出,从而导致无法退出APP

解决方案

网上很多博客建议使用fitsystemwindows,如这篇博客,但是因为当前APP是cordova项目,不能轻易更改cordova的webView初始化代码,此方案无效,即使实现也会有漏网之鱼。最终解决方案是在布局变化时(后台切回、键盘弹出收起等)重新计算页面高度,动态更新。从而解决问题。

参考资料: 参考资料

实施步骤

1. 重写StatusBar.java 的run方法,改后如下

 public void initialize(final CordovaInterface cordova, CordovaWebView webView) {
        LOG.v(TAG, "StatusBar: initialization");
        super.initialize(cordova, webView);

        this.cordova.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Window window = cordova.getActivity().getWindow();
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
                //添加内容end
                window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

            }
        });
    }

2. 将config.xml中的resizeOnFullScreen设置为false

此举是为了避免keyboard插件又重新计算一次,导致出错

3. 动态计算代码: FixScreen.java

  package com.tehang.TMC;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

import java.lang.reflect.Method;

public class FixScreen {
    public static void assistActivity(View content) {
        new FixScreen(content);
    }
    private View mChildOfContent;
    private int usableHeightPrevious;
    private ViewGroup.LayoutParams frameLayoutParams;
    private FixScreen(View content) {
        mChildOfContent = content;
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                possiblyResizeChildOfContent();
            }
        });
        frameLayoutParams = mChildOfContent.getLayoutParams();
    }
    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        Log.d("usableHeightNow", "possiblyResizeChildOfContent() returned: " + usableHeightNow);
        if (usableHeightNow != usableHeightPrevious) {
            frameLayoutParams.height = usableHeightNow;
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom);
    }
    public static boolean checkDeviceHasNavigationBar(Context context) {
        boolean hasNavigationBar = false;
        Resources rs = context.getResources();
        int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
        if (id > 0) {
            hasNavigationBar = rs.getBoolean(id);
        }
        try {
            Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
            Method m = systemPropertiesClass.getMethod("get", String.class);
            String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
            if ("1".equals(navBarOverride)) {
                hasNavigationBar = false;
            } else if ("0".equals(navBarOverride)) {
                hasNavigationBar = true;
            }
        } catch (Exception e) {
        }
        return hasNavigationBar;
    }
}

4. 在MainActivity.java中调用FixScreen,代码如下:

 @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Bundle extras = getIntent().getExtras();
        if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
            moveTaskToBack(true);
        }
        loadUrl(launchUrl);
        fixScreenOverlap();
        removeInviteTokenData();
    }

    /** 修复导航栏和状态栏引起的遮挡问题 */
    private void fixScreenOverlap(){
        if (FixScreen.checkDeviceHasNavigationBar(this)) {
            FixScreen.assistActivity(findViewById(android.R.id.content));
        }
    }

推荐阅读更多精彩内容