开发者社区> 奥特曼超人> 正文

android兼容huawei手机刘海屏解决方案

简介: 引用自华为官方文档:doc/50114 ,这里缩减了一些内容,捡取重要内容。 转载请标明出处: https://blog.
+关注继续查看

引用自华为官方文档:doc/50114 ,这里缩减了一些内容,捡取重要内容。
转载请标明出处:
https://blog.csdn.net/djy1992/article/details/80683575
本文出自:【奥特曼超人的博客】

推荐:

目录:

1. 背景

刘海屏指的是手机屏幕正上方由于追求极致边框而采用的一种手机解决方案。因形似刘海儿而得名。也有一些其他叫法:挖孔屏、凹口屏等,本文档统一按照刘海屏来命名。市场上已经有越来越多的手机都支持这种屏幕形式。

谷歌在安卓P版本中已经提供了统一的适配方案,可是在安卓O版本上如何适配呢?本文将详细介绍华为安卓O版本刘海屏适配方案。使用华为提供的刘海屏SDK进行适配,此方案也会继承到华为安卓P版本手机上。在华为P版本手机中将同时支持两种方案:华为O版本方案+谷歌P版本方案。另外因为安卓O版本的刘海屏手机已经在市场上大量上市,这些手机在市场上会存续2~3年。所以建议大家现在要同时适配华为O版本方案以及谷歌P版本方案。

奥特曼超人dujinyang

2. 华为已经发布的刘海屏手机信息

奥特曼dujinyang刘海屏

3. 华为刘海屏手机安卓O版本适配方案

设计理念:尽量减少APP的开发工作量

处理逻辑:
奥特曼dujinyang刘海屏

4. 华为刘海屏API接口

4.1 判断是否刘海屏
4.1.1 接口描述
奥特曼超人dujinyang刘海屏
是否是刘海屏手机:
true:是刘海屏 false:非刘海屏

4.1.2 调用范例

public static boolean hasNotchInScreen(Context context) {
    boolean ret = false;
    try {
        ClassLoader cl = context.getClassLoader();
        Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
        Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
        ret = (boolean) get.invoke(HwNotchSizeUtil);
    } catch (ClassNotFoundException e) {
        Log.e("test", "hasNotchInScreen ClassNotFoundException");
    } catch (NoSuchMethodException e) {
        Log.e("test", "hasNotchInScreen NoSuchMethodException");
    } catch (Exception e) {
        Log.e("test", "hasNotchInScreen Exception");
    } finally {
        return ret;
    }
}

4.2 获取刘海尺寸
4.2.1 接口描述
奥特曼超人dujinyang米奇云

4.2.2 调用范例

public static int[] getNotchSize(Context context) {
    int[] ret = new int[]{0, 0};
    try {
        ClassLoader cl = context.getClassLoader();
        Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
        Method get = HwNotchSizeUtil.getMethod("getNotchSize");
        ret = (int[]) get.invoke(HwNotchSizeUtil);
    } catch (ClassNotFoundException e) {
        Log.e("test", "getNotchSize ClassNotFoundException");
    } catch (NoSuchMethodException e) {
        Log.e("test", "getNotchSize NoSuchMethodException");
    } catch (Exception e) {
        Log.e("test", "getNotchSize Exception");
    } finally {
        return ret;
    }
}

4.3 应用页面设置使用刘海区显示
4.3.1 方案一
使用新增的Meta-data属性android.notch_support,在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效。

具体方式如下所示:

<meta-data android:name="android.notch_support" android:value="true"/>

对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:testOnly="false"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <meta-data android:name="android.notch_support" android:value="true"/>
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>

对Activity生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的Activity系统将不会做特殊处理:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:testOnly="false"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <activity android:name=".LandscapeFullScreenActivity" android:screenOrientation="sensor">
    </activity>
    <activity android:name=".FullScreenActivity">
        <meta-data android:name="android.notch_support" android:value="true"/>
    </activity>

4.3.2 方案二
使用给window添加新增的FLAG_NOTCH_SUPPORT

(1)接口1描述:应用通过增加华为自定义的刘海屏flag,请求使用刘海区显示

奥特曼超人dujinyang刘海屏

调用范例参考:

对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理:

/*刘海屏全屏显示FLAG*/
public static final int FLAG_NOTCH_SUPPORT=0x00010000;
/**
 * 设置应用窗口在华为刘海屏手机使用刘海区
 * @param window 应用页面window对象
 */
public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
    if (window == null) {
        return;
    }
    WindowManager.LayoutParams layoutParams = window.getAttributes();
    try {
        Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");
        Constructor con=layoutParamsExCls.getConstructor(LayoutParams.class);
        Object layoutParamsExObj=con.newInstance(layoutParams);
        Method method=layoutParamsExCls.getMethod("addHwFlags", int.class);
        method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException 
    | InvocationTargetException e) {
        Log.e("test", "hw add notch screen flag api error");
    } catch (Exception e) {
        Log.e("test", "other Exception");
    }
}

(2)接口2描述:可以通过clearHwFlags接口清除添加的华为刘海屏Flag,恢复应用不使用刘海区显示。

奥特曼超人dujinyang米奇云
调用范例参考:

/*刘海屏全屏显示FLAG*/
public static final int FLAG_NOTCH_SUPPORT=0x00010000;
/**
 * 设置应用窗口在华为刘海屏手机使用刘海区
 * @param window 应用页面window对象
 */
public static void setNotFullScreenWindowLayoutInDisplayCutout (Window window) {
    if (window == null) {
        return;
    }
    WindowManager.LayoutParams layoutParams = window.getAttributes();
    try {
        Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");
        Constructor con=layoutParamsExCls.getConstructor(LayoutParams.class);
        Object layoutParamsExObj=con.newInstance(layoutParams);
        Method method=layoutParamsExCls.getMethod("clearHwFlags", int.class);
        method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException 
    | InvocationTargetException e) {
        Log.e("test", "hw clear notch screen flag api error");
    } catch (Exception e) {
        Log.e("test", "other Exception");
    }
}

华为刘海屏flag动态添加和删除代码:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if(isAdd) {//add flag
            isAdd = false;
            NotchSizeUtil.setFullScreenWindowLayoutInDisplayCutout(getWindow());           
            getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
        } else{//clear flag
            isAdd = true;
            NotchSizeUtil.setNotFullScreenWindowLayoutInDisplayCutout(getWindow());            
            getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
        }
    }
});

使用和不使用刘海区效果对比:

奥特曼超人dujinyang米奇云

奥特曼超人dujinyang米奇云

4.4 获取默认和隐藏刘海区开关值接口
4.4.1 开关页面介绍
(1)老版本路径:系统设置→显示→显示区域控制

奥特曼超人dujinayng米奇云

(2)新版本路径:系统设置→显示→屏幕顶部显示
刘海屏华为解决方案

4.4.2 隐藏开关打开之后,显示规格
奥特曼超人dujinayng米奇云

4.4.3 读取开关状态调用范例
public static final String DISPLAY_NOTCH_STATUS = “display_notch_status”;
int mIsNotchSwitchOpen = Settings.Secure.getInt(getContentResolver(),DISPLAY_NOTCH_STATUS, 0);
// 0表示“默认”,1表示“隐藏显示区域”

5. 谷歌P版本刘海屏适配方案

5.1 特性介绍
谷歌称刘海屏为凹口屏以及屏幕缺口支持, 内容摘自:https://developer.android.com/preview/features#cutout
推荐:《android 兼容所有刘海屏的方案大全》

5.2 接口介绍
(1)获取刘海尺寸相关接口:

https://developer.android.com/reference/android/view/DisplayCutout

所属类 方法 接口说明

  • android.view.DisplayCutout List getBoundingRects() 返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。设备的每个短边最多只有一个非功能区域,而长边上则没有
  • android.view.DisplayCutout int getSafeInsetBottom() 返回安全区域距离屏幕底部的距离,单位是px。
  • android.view.DisplayCutout int getSafeInsetLeft() 返回安全区域距离屏幕左边的距离,单位是px。
  • android.view.DisplayCutout int getSafeInsetRight() 返回安全区域距离屏幕右边的距离,单位是px。
  • android.view.DisplayCutout int getSafeInsetTop() 返回安全区域距离屏幕顶部的距离,单位是px。

(2)设置是否延伸到刘海区显示接口:

https://developer.android.com/reference/android/view/WindowManager.LayoutParams#layoutInDisplayCutoutMode

奥特曼超人dujinayng米奇云

https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
奥特曼超人dujinayng刘海屏

5.3 参考实现代码
(1)设置使用刘海区显示代码:

getSupportActionBar().hide();
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 
//设置页面全屏显示
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = 
       windowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 
//设置页面延伸到刘海区显示
getWindow().setAttributes(lp);
注意:如果需要应用的布局延伸到刘海区显示,需要设置SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN。

属性:

奥特曼超人dujinayng刘海屏

(1)获取刘海屏安全显示区域和刘海尺寸信息:

contentView = getWindow().getDecorView().findViewById(android.R.id.content).getRootView();
contentView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
    @Override
    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
        DisplayCutout cutout = windowInsets.getDisplayCutout();
        if (cutout == null) {
            Log.e(TAG, "cutout==null, is not notch screen");//通过cutout是否为null判断是否刘海屏手机
        } else {
            List<Rect> rects = cutout.getBoundingRects();
            if (rects == null || rects.size() == 0) {
                Log.e(TAG, "rects==null || rects.size()==0, is not notch screen");
            } else {
                Log.e(TAG, "rect size:" + rects.size());//注意:刘海的数量可以是多个
                for (Rect rect : rects) {
                    Log.e(TAG, "cutout.getSafeInsetTop():" + cutout.getSafeInsetTop()
                            + ", cutout.getSafeInsetBottom():" + cutout.getSafeInsetBottom()
                            + ", cutout.getSafeInsetLeft():" + cutout.getSafeInsetLeft()
                            + ", cutout.getSafeInsetRight():" + cutout.getSafeInsetRight()
                            + ", cutout.rects:" + rect
                    );
                }
            }
        }
        return windowInsets;
    }
});

(3)说明:

通过windowInsets.getDisplayCutout()是否为null判断是否刘海屏手机,如果为null为非刘海屏:

Line 6203: 05-24 11:16:46.766 11036 11036 E Cutout_test: cutout==null, is not notch screen

如果是刘海屏手机可以通过接口获取刘海信息:

Line 6211: 05-24 11:11:16.839 10733 10733 E Cutout_test: cutout.getSafeInsetTop():126, cutout.getSafeInsetBottom():0, 
cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(414, 0 - 666, 126)

刘海个数可以是多个:

Line 6291: 05-24 11:27:04.517 11036 11036 E Cutout_test: rect size:2
Line 6292: 05-24 11:27:04.517 11036 11036 E Cutout_test: cutout.getSafeInsetTop():84, 
cutout.getSafeInsetBottom():84, cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(351, 0 - 729, 84)
Line 6293: 05-24 11:27:04.517 11036 11036 E Cutout_test: cutout.getSafeInsetTop():84, 
cutout.getSafeInsetBottom():84, cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(351, 1836 - 729, 1920)

6. UI适配

通过上面的2种方案(meta-data或者Flag),应用在华为刘海屏手机上就能默认使用刘海区显示了,为了避免UI被刘海区遮挡,应用还要进一步进行UI适配:

6.1 判断是否为刘海屏
判断是否为刘海屏?可以调用华为刘海屏API,参考4.1。

6.2 调整布局
如果是刘海屏,调整布局避开刘海区。

布局原则:保证重要的文字、图片、视频信息、可点击的控件和图标,应用弹窗等,建议显示在状态栏区域以下(安全区域)。如果内容不重要或者不会遮挡,布局可以延伸到状态栏区域(危险区域)。

建议按照如下布局原则修改:

奥特曼超人dujinyang

获取系统状态栏高度接口:

public static int getStatusBarHeight(Context context) {
    int result = 0;
    int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = context.getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

建议窗口显示内容在状态栏区域以下的另外一个原因:华为有一个自研的隐藏刘海的需求,可以参考章节4.4

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
西弗吉尼亚州率先实施区块链移动投票解决方案
西弗吉尼亚州率先实施区块链移动投票解决方案
1082 0
android用eclipse开发碰到65535问题的完美解决方案
android用eclipse开发碰到65535问题的完美解决方案
14 0
has been modified since the precompiled header问题的几种解决方案
有时候不小心就报了这个错误,在以下步骤中,只有没解决问题的情况下,才继续往下操作: Clean工程 按住option键,再次Clean(Clean Build Folder.
713 0
专注射频集成电路设计,「米乐为微电子」提供高集成度芯片解决方案
其产品覆盖了全频段(DC -110GHz)以及全品类,具有超宽频、跨多个工艺线的技术优势。
411 0
视频直播推流拉流慢、卡顿解决方案
视频直播类App当前已经普遍采用CDN来实现访问加速,但还是经常遇到推拉流慢、卡顿的问题。这类问题一般是由于调度不精准、域名劫持、终端手机接入网络动态切换等因素导致,结合使用CDN和HTTPDNS可以比较完美解决此类问题。
23032 0
sql server 索引阐述系列六 碎片查看与解决方案
原文:sql server 索引阐述系列六 碎片查看与解决方案 一 . dm_db_index_physical_stats 重要字段说明   1.1 内部碎片:是avg_page_space_used_in_percent字段。
772 0
Android弹幕编程设计实现的解决方案(一)
 Android弹幕编程设计实现的解决方案(一) 在现在的一些视频类网站、视频类直播网站,比如A站和B站,当视频在播放的时候,会在屏幕上出现一些滚动的字幕,这些字幕是UGC,通常是用户的评论,称之为“弹幕”,这些弹幕一般从右往左滚动,以符合人类的阅读习惯。
859 0
+关注
奥特曼超人
KARL-Dujinyang-奥特曼超人。作者目前任职于银狐、泡椒游戏公司,曾任职腾为CTO,CSDN技术专家,曾创办过创业go、造梦工厂、米奇云,万能孵化器等,领域涉及逆向安全专家以及全栈工程师,作者目前出书中。
239
文章
7
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载