刘海屏的适配

  • 关于刘海屏适配问题、android跟风刘海屏、在android P(9.0 API 28)才能用原生的android 刘海屏适配、在Android N(7.0 API 24)到 android P(9.0 API 28)期间、很多厂商都对自己的硬件做了提前的适配,所以需要我们对其做适配、
  • 何时才需要适配、设置全屏模式下,需要对刘海屏进行适配。不然会存在这样的问题U展示不全问题、在android N 以下的设置全屏没问题。在N - P 有刘海屏情况下 则会有UI显示异常(底部ui显示不全)。故设置一个包裹容器包裹、
  • 怎么适配呢、
  1. 判断版本区间(android N 以下、android N - android P、 Android P 以上)
  2. android N 以下, 直接默认的设置全屏。 android N 以上 直接如果不是刘海屏 则设置为全屏,如果是刘海屏则不进行设置。
requestWindowFeature(Window.FEATURE_NO_TITLE)
            window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN)
复制代码
  1. 不同的厂商刘海屏的适配也不同具体参考对应的开发者平台。以下代码说明如何查询是否是刘海屏,以及获取刘海屏的高度。若类似于一加手机没有开发者平台交流的、只好单独拎出来,判断。

获取到刘海屏的高度、若没有,则返回0

// ------------------------------ 刘海屏处理 ------------------------------
    private static final String ONEPLUS6 = "ONEPLUS A6000";

    /**
     * 获取刘海的高度
     *
     * @param context
     * @return
     */
    public static int obtainCutoutHeight(Context context) {
        // 7.0以下不会有刘海,避免不必要的反射性能损耗
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            return 0;
        }
         /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // ------------------------------ Android P 版本 及以上 的统一适配 ------------------------------
            val displayCutout: DisplayCutout? = rootWindowInsets.displayCutout
            if (displayCutout != null) {
                L.e("TAG", "安全区域距离屏幕顶部的距离 SafeInsetTop:" + displayCutout.safeInsetTop)
                return displayCutout.safeInsetTop
            }
            return 0
        }*/

        if (OSUtils.isEMUI() && OSUtils.hasHWNotchInScreen(context)) {
            return OSUtils.getHWNotchSize(context)[1];
        }

        if (OSUtils.isMIUI() && OSUtils.hasMIUINotchInScreen()) {
            return OSUtils.getMIUINotchSize(context);
        }

        // oppo固定80px
        if (OSUtils.hasOppoNotchInScreen(context)) {
            return 80;
        }

        if (OSUtils.hasVivoNotchInScreen(context)) {
            return obtainStatusBarHeight(context);
        }

        if (ONEPLUS6.equals(Build.MODEL)) {
            return obtainStatusBarHeight(context);
        }

        return 0;
    }

    public static int obtainStatusBarHeight(Context context) {
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        int statusBarHeight = 0;
        if (resourceId > 0) {
            statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
        }
        return statusBarHeight;
    }
复制代码

判断是不是华为

/**
     * 判断是否为emui
     * Is emui boolean.
     *
     * @return the boolean
     */
    public static boolean isEMUI() {
        String property = getSystemProperty(KEY_EMUI_VERSION_NAME, "");
        return !TextUtils.isEmpty(property);
    }
复制代码

华为是否有刘海屏

activity 标签下添加
<meta-data android:name="android.notch_support" android:value="true"/>
复制代码
/**
     * 判断是否是华为刘海屏
     *
     * @param context
     * @return
     */
    public static boolean hasHWNotchInScreen(Context context) {

        boolean hasNotch = false;
        try {
            ClassLoader classLoader = context.getClassLoader();
            Class hwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method method = hwNotchSizeUtil.getMethod("hasNotchInScreen");
            hasNotch = (Boolean) method.invoke(hwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return hasNotch;
    }

复制代码

获取华为刘海屏高度

/**
     * 获取华为刘海的高宽
     *
     * @param context 上下文对象
     * @return [0]值为刘海宽度int;[1]值为刘海高度
     */
    public static int[] getHWNotchSize(Context context) {
        int[] size = new int[2];
        try {
            ClassLoader classLoader = context.getClassLoader();
            Class hwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method method = hwNotchSizeUtil.getMethod("getNotchSize");
            size = (int[]) method.invoke(hwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return size;
    }


复制代码

MIUI 是否有刘海屏、及刘海屏的高度

/**
     * 判断是否为miui
     * Is miui boolean.
     *
     * @return the boolean
     */
    public static boolean isMIUI() {
        String property = getSystemProperty(KEY_MIUI_VERSION_NAME, "");
        return !TextUtils.isEmpty(property);
    }
    
/**
     * 只适用于判断MIUI 是否有刘海屏
     * SystemProperties.getInt("ro.miui.notch", 0) == 1;
     *
     * @return
     */
    public static boolean hasMIUINotchInScreen() {
        try {
            Class<?> clz = Class.forName("android.os.SystemProperties");
            Method get = clz.getMethod("getInt", String.class, String.class);
            return (int) get.invoke(clz, "ro.miui.notch", -1) == 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 获取MIUI 的刘海屏高度
     *
     * @param context
     */
    public static int getMIUINotchSize(Context context) {
        int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");
        if (resourceId > 0) {
            return context.getResources().getDimensionPixelSize(resourceId);
        }
        return 0;
    }
复制代码

oppo 是否有刘海屏、并统一设置为80px

/**
     * 是否是Oppo的刘海屏
     *
     * @param context
     * @return
     */
    public static boolean hasOppoNotchInScreen(Context context) {
        return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
    }

复制代码

vivo 是否支持刘海屏、 并设置高度为状态栏高度

/**
     * Vivo手机刘海屏
     *
     * @param context
     * @return
     */
    /**
     * 是否有刘海
     */
    public static final int VIVO_NOTCH = 0x00000020;
    /**
     * 是否有圆角
     */
    public static final int VIVO_FILLET = 0x00000008;

    public static boolean hasVivoNotchInScreen(Context context) {

        boolean ret = false;
        try {
            ClassLoader classLoader = context.getClassLoader();
            Class FtFeature = classLoader.loadClass("android.util.FtFeature");
            Method method = FtFeature.getMethod("isFeatureSupport", int.class);
            ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
        } catch (ClassNotFoundException e) {
            Log.e("Notch", "hasNotchAtVoio ClassNotFoundException");
        } catch (NoSuchMethodException e) {
            Log.e("Notch", "hasNotchAtVoio NoSuchMethodException");
        } catch (Exception e) {
            Log.e("Notch", "hasNotchAtVoio Exception");
        } finally {
            return ret;
        }
    }
复制代码

自定义包裹的LinearLayout

/**
 * @author:Created by Mrko
 * @description: 刘海屏适配类
 */
class DisplayCutoutView : LinearLayout {
    private lateinit var emptyView: View

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initView()
    }

    fun initView() {
        val viewParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                StatusBarUtils.obtainCutoutHeight(context))
        emptyView = View(context)
        emptyView.layoutParams = viewParams
        addView(emptyView)
    }

}
复制代码

运用地方

<com.xxx.DisplayCutoutView
        android:id="@+id/rl_display"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/primary_pressed"
        android:orientation="vertical">

        <com.xxx.hintView
            android:id="@+id/color_detail_hint"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="@color/white"
            android:textSize="14sp" />
    </com.xxx.DisplayCutoutView>

复制代码

Activity 进行兼容性适配

override fun beforeSetContentView() {

        /*L.e("mrko--->", "手机品牌--> \n  ${Build.PRODUCT} + \n
                                        "${Build.DEVICE} + \n
                                        "${Build.BOARD} + \n
                                        "${Build.MANUFACTURER} + \n
                                        "${Build.BRAND} + \n
                                        "${Build.MODEL} + \n
                                        "${Build.HARDWARE} + \n
                                        "${Build.MANUFACTURER} + \n
                                        "${Build.ID} + \n
                                        "${Build.DISPLAY} + \n ")*/
        if (StatusBarUtils.obtainCutoutHeight(mContext) == 0) {
            requestWindowFeature(Window.FEATURE_NO_TITLE)
            window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN)
        }
        super.beforeSetContentView()
    }
复制代码