刘海屏之全面屏适配攻略

                                      刘海屏之全面屏攻略

前言

由于苹果公司的“先进设计”导致各大手机厂商纷纷跟风其设计,导致Android的屏幕适配出现新的刚需——刘海屏的适配。为了简化这些适配操作以及繁琐的判断封装优化出一个工具库:BangScreenToolsMaster

适配方案及原理

适配流程

刘海屏之全面屏适配攻略_第1张图片

AndroidP的适配方式:

AndroidP及以上的方式必须适用于sdk大于等于28的情况下使用。

显示模式:

Android P中新增了一个布局参数属性layoutInDisplayCutoutMode,包含了三种不同的模式

LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 只有当DisplayCutout完全包含在系统栏中时,才允许窗口延伸到DisplayCutout区域。 否则,窗口布局不与DisplayCutout区域重叠
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不使用刘海屏区域
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 该窗口始终允许延伸到屏幕短边上的DisplayCutout区域。
方法 接口描述
getBoundingRects() 返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。
getSafeInsetLeft () 返回安全区域距离屏幕左边的距离,单位是px。
getSafeInsetRight () 返回安全区域距离屏幕右边的距离,单位是px。
getSafeInsetTop () 返回安全区域距离屏幕顶部的距离,单位是px。
getSafeInsetBottom() 返回安全区域距离屏幕底部的距离,单位是px。

AndroidP以下的华为适配方案:

使用新增的meta-data属性android.notch_support。在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效。
如下所示:

  • Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理。
  • Activity生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的Activity系统将不会做特殊处理。

华为的适配使用了反射相关操作代码如下:

//判断是否是华为手机
 public final boolean isHuaWei() {
        String manufacturer = Build.MANUFACTURER;
        if (!TextUtils.isEmpty(manufacturer)){
            if (manufacturer.contains("HUAWEI")) return true;
        }
        return false;
    }
//判断是否存有刘海屏
@RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public boolean hasNotBangScreen(Window window) {
        if (isHaveResult) return isBangScreen;
        try {
            ClassLoader huaWeiClassLoader = window.getContext().getClassLoader();
            Class HwNotchSizeUtil = huaWeiClassLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method method = HwNotchSizeUtil.getMethod("hasNotchInScreen");
            isHaveResult = true;
            return isBangScreen = (boolean) method.invoke(HwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "hasNotchInScreen ClassNotFoundException");
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "hasNotchInScreen NoSuchMethodException");
        } catch (Exception e) {
            Log.e(TAG, "hasNotchInScreen Exception");
        } finally {
            isHaveResult = true;
            return isBangScreen;
        }
    }
//获取刘海屏尺寸相关信息
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public List getBangSize(Window window) {
        ArrayList result = new ArrayList();
        if (window != null) {
            Rect rect = new Rect();
            try {
                Context context = window.getContext();
                if (hwBangSizeUtil == null && context != null) {
                    ClassLoader cl = context.getClassLoader();
                    hwBangSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
                }
                if (hwBangSizeUtil == null) {
                    return result;
                }
                Method get = hwBangSizeUtil.getMethod("getNotchSize");
                int[] ret = (int[]) get.invoke(hwBangSizeUtil);
                if (ret == null) {
                    return result;
                } else {
                    Resources resources = context.getResources();
                    if (resources != null) {
                        rect.left = (resources.getDisplayMetrics().widthPixels - ret[0]) / 2;
                        rect.bottom = ret[1];
                        rect.right = rect.left + ret[0];
                        rect.top = 0;
                        result.add(rect);
                    }
                    return result;
                }
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                return result;
            }
        } else return result;
    }


AndroidP以下的VIVO适配方案:

vivo在设置--显示与亮度--第三方应用显示比例中可以切换是否全屏显示还是安全区域显示

相关代码如下:

//判断是不是Vivo手机
public final boolean isVivo() {
        String manufacturer = this.getSystemProperty("ro.vivo.os.name");
        if (!TextUtils.isEmpty(manufacturer)){
            return true;
        }
        return false;
    }
//获取手机是否是刘海屏手机
  @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public boolean hasNotBangScreen(Window window) {
        if (window == null)
            return false;
        if (vivo == null) {
            ClassLoader vivoLoader = window.getContext().getClassLoader();
            try {
                vivo = vivoLoader.loadClass("android.util.FtFeature");
                vivoMethod = vivo.getMethod("isFeatureSupport", Integer.TYPE);
                return (boolean) vivoMethod.invoke(vivo, 0x00000020);
            } catch (ClassNotFoundException e) {
                logError(e);
                return false;
            } catch (NoSuchMethodException e) {
                logError(e);
                return false;
            } catch (IllegalAccessException e) {
                logError(e);
                return false;
            } catch (InvocationTargetException e) {
                logError(e);
                return false;
            }
        } else {
            if (vivoMethod == null) {
                try {
                    vivoMethod = vivo.getMethod("isFeatureSupport", Integer.TYPE);
                } catch (NoSuchMethodException e) {
                    logError(e);
                    return false;
                }
                try {
                    return (boolean) vivoMethod.invoke(vivo, 0x00000020);
                } catch (IllegalAccessException e) {
                    logError(e);
                    return false;
                } catch (InvocationTargetException e) {
                    logError(e);
                    return false;
                }
            }
        }
        return false;
    }
//获取刘海屏尺寸信息

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public List getBangSize(Window window) {
        List result = new ArrayList<>();
        if (window == null) return result;
        Rect rect = new Rect();
        DisplayMetrics displayMetrics = window.getContext().getResources().getDisplayMetrics();
        int notchWidth = (int) TypedValue.applyDimension(1, 100.0F, displayMetrics);
        int notchHeight = (int) TypedValue.applyDimension(1, 27.0F, displayMetrics);
        rect.left = (displayMetrics.widthPixels - notchWidth) / 2;
        rect.right = rect.left + notchWidth;
        rect.top = 0;
        rect.bottom = notchHeight;
        result.add(rect);
        return result;
    }

AndroidP以下的小米适配方案:

//判断是否是小米
  public final boolean isMiui() {
        String manufacturer = getSystemProperty("ro.miui.ui.version.name");
        if (!TextUtils.isEmpty(manufacturer)){
            return true;
        }
        return false;
    }
//判断是否是刘海屏
@RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public boolean hasNotBangScreen(Window window) {
        return "1".equals(SystemProperties.getSingle().get("ro.miui.notch"));
    }
//由于小米的状态栏高度略大于或等于刘海屏高度故这样封装

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public List getBangSize(Window window) {
        List result = new ArrayList<>();
        if (window == null) return result;
        Context context = window.getContext();
        Resources resources = context.getResources();
        Rect rect = new Rect();
        if (resources != null) {
            rect.left = 0;
            rect.bottom = BangScreenTools.getBangScreenTools().getStatusBarHeight(context);
            rect.right = resources.getDisplayMetrics().widthPixels;
            rect.top = 0;
            result.add(rect);
        }
        return result;
    }

AndroidP以下的oppo适配方案:

//判断是否是oppo   
 public final boolean isOppo() {
        String manufacturer = getSystemProperty("ro.product.brand");
        if (!TextUtils.isEmpty(manufacturer)){
            return true;
        }
        return false;
    }
//判断是否是刘海屏
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public boolean hasNotBangScreen(Window window) {
        if (window == null) return false;
        return window.getContext().getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
    }
    //目前Oppo刘海屏机型尺寸规格都是统一的,显示屏宽度为1080px,高度为2280px,刘海区域宽度为324px, 高度为80px
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public List getBangSize(Window window) {
        List result = new ArrayList<>();
        if (window == null) return result;
        DisplayMetrics displayMetrics = window.getContext().getResources().getDisplayMetrics();
        Rect rect = new Rect();
        int width = 324;
        int height = 80;
        rect.left = (displayMetrics.widthPixels - width) / 2;
        rect.right = rect.left + width;
        rect.bottom = height;
        rect.top = 0;
        result.add(rect);
        return result;
    }

BangScreenToolsMaster封装库的使用:

make the statusbar was transparent

//used onCreat method
    BangScreenTools.getBangScreenTools().transparentStatusCutout(window, this)
//used onWindowFocusChanged method
    BangScreenTools.getBangScreenTools().windowChangeTransparentStatusCutout(window)

make the layout extend statusbar

//used onCreat method
    BangScreenTools.getBangScreenTools().extendStatusCutout(window, this)
//used onWindowFocusChanged method
    BangScreenTools.getBangScreenTools().windowChangeExtendStatusCutout(window)

make the layout was fullscreen

//used onCreat method
    BangScreenTools.getBangScreenTools().fullscreen(window, this)
//used onWindowFocusChanged method,this void is make fullscreen is worked.
    BangScreenTools.getBangScreenTools().windowChangeFullscreen(window)

make the layout not use bangscreen

//used onCreat method
    BangScreenTools.getBangScreenTools().blockDisplayCutout(window)

这是AndroidP相关Api的介绍接下来展示下封装后代码运行的效果

 

你可能感兴趣的:(Android刘海屏适配)