魅族Flyme7系统刘海屏app适配文档

2019-01-23  本文已影响0人  2e0bf3cf6d2d

一, 需求

  1. 竖屏有状态栏时由状态栏调整其布局来适配刘海区
  2. 横屏或竖屏无状态栏时默认情况下由系统平移界面来避开刘海区
  3. 若要在上述状态下, 显示到刘海区域。 则应用需要适配.
11.png

二, 应用适配方法(广大app开发者通过此方法适配即可)

1. 适配刘海区域

若要在竖屏无状态栏情况下使用刘海区域。 应用可在页面进行如下配置。

// Android P中已增加适配刘海屏的原生接口支持。 设置此方式只在Android P以下版本有效。
if (Build.VERSION.SDK_INT < 28) {
   View decor = getWindow().getDecorView();
   decor.setSystemUiVisibility(decor.getSystemUiVisibility() | 0x00000080);
}
//注1: 此逻辑的关键在于为systemUiFlag添加 0x00000080, 请应用在操作时请不要将此字段覆盖。
//注2:据同事反馈在Android M和L中, setSystemUiVisibility与requestWindowFeature共用时, 
//     需将setSystemUiVisibility置于requestWindowFeature之后, 否则会闪退。

2. 判断是否刘海设备

// 判断刘海设备
boolean fringeDevice = false;
try {
  Class clazz = Class.forName("flyme.config.FlymeFeature");
  Field field = clazz.getDeclaredField("IS_FRINGE_DEVICE");
  fringeDevice = (boolean) field.get(null);
} catch (Exception e) {
  Log.e("ERROR", e.toString());
}

3. 获取刘海区域大小

// 获取刘海高度(51px)
int fringeHeight = 0;
int fhid = getResources().getIdentifier("fringe_height", "dimen", "android");
if (fhid > 0) {
  fringeHeight = getResources().getDimensionPixelSize(fhid);
}
// 获取刘海宽度(534px,)
int fringeWidth = 0;
int fwid = getResources().getIdentifier("fringe_width", "dimen", "android");
if (fwid > 0) {
  fringeWidth = getResources().getDimensionPixelSize(fwid);
}

4. 判断隐藏刘海开关状态

// 判断隐藏刘海开关(默认关)
boolean isFringeHidden = Settings.Global.getInt(getContentResolver(), "mz_fringe_hide",0) == 1;

5. 监听刘海开关状态

Uri uri = Settings.Global.getUriFor("mz_fringe_hide");
getContentResolver().registerContentObserver(uri, false, new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        boolean isHidden = Settings.Global.getInt(getContentResolver(), "mz_fringe_hide",0) == 1;
        //TODO 监听获取
    }
});

三, 系统获取方法

1. 判断是否刘海设备

// 判断刘海设备
boolean fringeDevice = flyme.config.FlymeFeature.IS_FRINGE_DEVICE;

2. 获取刘海区域大小

// 获取刘海高度(51px)
int fringeHeight = getResources().getDimensionPixelSize(com.android.internal.R.dimen.fringe_height);
// 获取刘海宽度(534px,)
int fringeWidth = getResources().getDimensionPixelSize(com.android.internal.R.dimen.fringe_width);

3. 判断隐藏刘海开关状态

// 判断隐藏刘海开关(默认关)
boolean isFringeHidden = Settings.Global.getInt(mContext.getContentResolver(),MzSettings.Global.MZ_FRINGE_HIDE, 0) == 1;

四, 系统弹窗

框架对系统弹窗也做了处理。 但是仅限layer层级低于StatusBar的窗口, 具体参见WindowManagerPolicy.java 的 getWindowLayerFromTypeLw 方法中的定义

1. 处理逻辑

12.png

这里注意与上面不同:我们为系统弹窗赋予了更高的权限, 系统弹窗若做了适配, 则无视隐藏刘海开关状态, 始终按原始方式显示

2. 适配方法

View view = new View(context);
if (Build.VERSION.SDK_INT < 28) {
    view.setSystemUiVisibility( view.getSystemUiVisibility() | 0x00000080);
}
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowManager.addView(view, params);

此处需注意对setSystemUiVisibility的设置需位于 addView 之前。 且一经设置, 不容改变

五, 特殊需求

1, 忽略隐藏刘海开关

在我们的原需求中, 如果用户在设置中手动开启了“隐藏刘海”开关。 则无论应用是否(应用二.1中的方法)适配刘海屏, 都会在顶部显示黑色圆角以隐藏刘海。但是在某些重要场景中, 应用希望自行处理隐藏刘海的情况, 如相机, 图库。。

应用可以使用如下方法来屏蔽系统关于刘海开关的相关处理。 (请使用201808241800之后的版本进行验证。 )

if (Build.VERSION.SDK_INT < 28) {
   View decor = getWindow().getDecorView();
   decor.setSystemUiVisibility(decor.getSystemUiVisibility() | 0x00000080 | 0x00000040);
}
// 0x00000040需要与0x00000080配合使用, 单独使用无效。

注意: 为了保证系统功能的一致性, 请谨慎使用此功能

开发者可以购买 魅族X8 来进行问题的验证

https://item.jd.com/100000827661.html

上述文档仅仅针对与Android8.0的机器有效, Android9.0已经官方公开了刘海屏的接口,相关的适配参考如下:

针对当前android 9.0的刘海屏, 有很多应用出现了界面的平移现象. 这主要是由Android P的原生逻辑来控制的. 所以需要应用对刘海屏进行适配. 适配方法如下

一. 原生判断是否刘海屏方式

由于Android P对异型屏各种情况的考虑, 比如有多处异型区域, 异型区域不规则等等情况. 系统中引入了 DisplayCutout 和 LAYOUT_IN_DISPLAY_CUTOUT_MODE 方便应用去适配. 判断方式如下:

DisplayCutout displayCutout = decorView.getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
    Log.e("TAG", "安全区域距离屏幕左边的距离 SafeInsetLeft:" + displayCutout.getSafeInsetLeft());
    Log.e("TAG", "安全区域距离屏幕右部的距离 SafeInsetRight:" + displayCutout.getSafeInsetRight());
    Log.e("TAG", "安全区域距离屏幕顶部的距离 SafeInsetTop:" + displayCutout.getSafeInsetTop());
    Log.e("TAG", "安全区域距离屏幕底部的距离 SafeInsetBottom:" + displayCutout.getSafeInsetBottom());
    List<Rect> rects = displayCutout.getBoundingRects();
    if (rects == null || rects.size() == 0) {
        Log.e("TAG", "不是刘海屏");
    } else {
        Log.e("TAG", "刘海屏数量:" + rects.size());
        for (Rect rect : rects) {
            Log.e("TAG", "刘海屏区域:" + rect);
        }
    }
} else {
    Log.e("TAG", "不是刘海屏!!!!!!!!!!!!!!!!!!!!!!");
}

若在onCreate中获取, 请使用decorView.post(new Runnable() { ....})

二. Flyme判断刘海屏方式

鉴于一方面仍然有许多应用反馈按照原生的方式得到为null,另一方面使用原生的判断方式时机可能比较晚,这里提供flyme的一种判定方式


 DisplayManager dy = (DisplayManager) getApplicationContext().getSystemService(Context.DISPLAY_SERVICE);
        Display d = dy.getDisplay(Display.DEFAULT_DISPLAY);
        int FLAG_DISPLAY_CUTOUT = 1 << 29;
        if ((d.getFlags() & FLAG_DISPLAY_CUTOUT) != 0) {
                                    //是刘海屏
        } else {
                                    //不是刘海屏
        }

二. 异型屏适配.

请各应用按需使用设置

WindowManager.LayoutParams lp = getWindow().getAttributes();
// WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
// WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
// WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
lp.layoutInDisplayCutoutMode =WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
getWindow().setAttributes(lp);

至于使用各模式的效果, 请查看附件demo

上一篇 下一篇

猜你喜欢

热点阅读