Android刘海屏适配及view被摄像头遮挡动态改变位置

2023-10-18 02:20

本文主要是介绍Android刘海屏适配及view被摄像头遮挡动态改变位置,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android刘海屏适配及view被摄像头遮挡动态改变位置

目前市面上的刘海屏、水滴屏、挖孔屏越来越多,作为移动开发者来说,这并不是一件好事,越来越多异形屏的出现意味着我们需要投入大量的经历在屏幕适配上,本文总结了当下主流手机的屏幕适配方式(华为、vivo、oppo、小米)以及判断view是否被摄像头遮挡,去动态改变view的位置。

一. Android P及以上

谷歌官方从Android P开始给开发者提供了刘海屏相关的API,可以通过直接调用API来进行刘海屏的适配处理。通过DisplayCutout类可以获得安全区域的范围以及刘海区域(官方的叫法是缺口)的信息,只有API Level在28及以上才可以调用。

1. 判断是否是异形屏

    /*** Android P异形屏判断** @param activity activity* @return 是否是异形屏*/public static boolean isSpecialScreen(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {final View decorView = activity.getWindow().getDecorView();if (decorView != null) {WindowInsets windowInsets = decorView.getRootWindowInsets();if (windowInsets != null) {DisplayCutout cutoutDisp = windowInsets.getDisplayCutout();if(cutoutDisp != null) {displayCutout = cutoutDisp;return true;}}}}return false;}

2. 获取异形屏的刘海高度

    /*** 9.0以上手机,获取异形屏的刘海高度** @return 获取异形屏的刘海高度*/@RequiresApi(28)private static int getNotchHeightFromP() {int notchHeight = 0;if (SwanApp.get() == null || SwanApp.get().getActivity() == null) {return notchHeight;}try {View view = SwanApp.get().getActivity().getWindow().getDecorView();WindowInsets windowInsets = view.getRootWindowInsets();if (windowInsets == null) {return notchHeight;}DisplayCutout displayCutout = windowInsets.getDisplayCutout();if (displayCutout == null) {return notchHeight;}notchHeight = displayCutout.getSafeInsetTop();if (DEBUG) {Log.d(TAG, "刘海屏高度:" + notchHeight);}} catch (Exception e) {if (DEBUG) {Log.w(TAG, e);}}return notchHeight;}

二. Android P以下

对于Android P以下的手机,我们可以通过各个厂商提供的适配方案来进行适配。

1. 华为适配方案

    /*** 判断华为是否有刘海屏** @param context Context* @return hasNotch 是否有刘海屏*/private static boolean hasNotchAtHuawei(@NonNull Context context) {boolean ret = false;try {ClassLoader classLoader = context.getClassLoader();Class HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");ret = (boolean) get.invoke(HwNotchSizeUtil);} catch (Exception e) {if (DEBUG) {e.printStackTrace();}}return ret;}

2. VIVO适配方案

    /*** 判断VIVO是否有刘海屏* <p>* 0x00000020表示是否有凹槽;* 0x00000008表示是否有圆角。** @param context Context* @return hasNotch 是否有刘海屏*/private static boolean hasNotchAtVivo(@NonNull 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 (Exception e) {if (DEBUG) {e.printStackTrace();}}return ret;}

3. 小米适配方案

    /*** 判断MIUI是否有刘海屏** @param context Context* @return 是否有刘海屏*/private static boolean hasNotchAtMiui(@NonNull Context context) {boolean ret = false;try {ClassLoader classLoader = context.getClassLoader();Class SystemProperties = classLoader.loadClass("android.os.SystemProperties");Method getInt = SystemProperties.getMethod("getInt", String.class, int.class);ret = (Integer) getInt.invoke(SystemProperties, "ro.miui.notch", 0) == 1;} catch (Exception e) {if (DEBUG) {e.printStackTrace();}}return ret;}

4. OPPO适配方案

    /*** 判断OPPO是否有刘海屏** @param context Context* @return 是否有刘海屏*/private static boolean hasNotchAtOPPO(@NonNull Context context) {return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");}

三. 设置异形屏的显示模式

Android 还允许我们控制是否在刘海区域内显示内容。窗口布局属性 layoutInDisplayCutoutMode 控制您的内容如何呈现在刘海区域中。您可以将 layoutInDisplayCutoutMode 设为以下某个值:

LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - 这是默认行为,在竖屏模式下,内容会呈现到刘海区域中;但在横屏模式下,内容会显示黑边。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - 在竖屏模式和横屏模式下,内容都会呈现到刘海区域中。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER - 内容从不呈现到刘海区域中。

    /*** 设置异形屏的显示模式**@param activity Activity*@param int mode(三种模式)*/public static void setWindowLayoutInDisplayCutoutMode(Activity activity,int mode) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {WindowManager.LayoutParams lp = activity.getWindow().getAttributes();lp.layoutInDisplayCutoutMode = mode;activity.getWindow().setAttributes(lp);}}

四. 判断view是否被摄像头遮挡

刘海屏的摄像头可能在左边、中间、右边,我们可以通过displayCutout.getBoundingRects()获取所有缺口的集合,然后根据缺口的位置判断view是否被遮挡。

    /**** 判断view是否被摄像头遮挡*/public static boolean isViewCovered(Activity activity,View view) {//判断是否是异形屏(里面包含Android P以上及以下异形屏的判断)if (isSpecialshapedScreen(activity) && isAndroidP(activity)) {List<Rect> rects = displayCutout.getBoundingRects();int rectLfet = rects.get(0).left;int rectRight = rects.get(0).right;int rectTop = rects.get(0).top;int rectBottom = rects.get(0).bottom;int[] location = new  int[2] ;view.getLocationOnScreen(location); //获取在整个屏幕内的绝对坐标,含statusBarint width = view.getWidth();int height = view.getHeight();int left = location[0];int right = location[0] + width;int top = location[1];int bottom = location[1] + height;if (((bottom <= rectBottom && bottom > rectTop) || (top < rectBottom && top >= rectTop))&& ((right > rectLfet && right <= rectRight)|| (left >= rectLfet && right <= rectRight)|| (left >= rectLfet && left < rectRight)|| (left < rectLfet && right > rectRight))) {return true;} else if (((left >= rectLfet && left < rectRight) || (right > rectLfet && right <= rectRight))&& ((bottom > rectTop && bottom <=rectBottom)|| (top >= rectTop && bottom <= rectBottom)|| (top >= rectTop && top < rectBottom)|| (top < rectTop && bottom > rectBottom))) {return true;} else if (left <= rectLfet && right >= rectRight && top <= rectTop && bottom >= rectBottom) {return true;}}return false;}
分析:
left 、top、right、bottom分别代表view在屏幕中的左上右下距离。
rectLeft、rectTop、rectRight、rectBottom分别代表刘海屏在屏幕中的左上右下距离。

1.以view的上边或下边为边界,上边或下边在刘海屏的上下之间,view被遮挡的情况有四种。
2.以view的左边或右边为边界,左边或右边在刘海屏的左右之间,view被遮挡的情况有四种。
3.刘海屏被view包围
在这里插入图片描述

五. 动态改变view的位置

当我们判断view被刘海屏遮挡后,就可以改变view的位置,根据需求移动到刘海屏下方或其他。可以通过设置layoutParams,要想获取到view在屏幕中的位置,需要等view绘制完成,所以使用view.post

    /** *伪代码*/view.post(new Runnable() {@Overridepublic void run() {if (isViewCovered(activity,view) ) {List<Rect> rects = cutoutDisp.getBoundingRects();int rectBottom = rects.get(0).bottom;FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();layoutParams.topMargin = mMenu.getTop() + rectBottom;mMenu.setLayoutParams(layoutParams);}}});

Over!!!

这篇关于Android刘海屏适配及view被摄像头遮挡动态改变位置的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/229547

相关文章

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

python3如何找到字典的下标index、获取list中指定元素的位置索引

《python3如何找到字典的下标index、获取list中指定元素的位置索引》:本文主要介绍python3如何找到字典的下标index、获取list中指定元素的位置索引问题,具有很好的参考价值,... 目录enumerate()找到字典的下标 index获取list中指定元素的位置索引总结enumerat

Python如何将OpenCV摄像头视频流通过浏览器播放

《Python如何将OpenCV摄像头视频流通过浏览器播放》:本文主要介绍Python如何将OpenCV摄像头视频流通过浏览器播放的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完... 目录方法1:使用Flask + MJPEG流实现代码使用方法优点缺点方法2:使用WebSocket传输视

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级