(1)屏幕的正上方居中位置(下图黑色区域)会被挖掉一个孔,屏幕被挖掉的区域无法正常显示内容,这种类型的屏幕就是刘海屏,也有其他叫法:挖孔屏、凹凸屏等等,这里统一按刘海屏命名。
(2)现在市场上的情况来说,“刘海屏”主要分成两类,一类是标准的 Android P Api,另外一类就是厂商在 Android P 以下的系统,做的特殊适配。
(1)对于有状态栏的页面:不会受到刘海屏特性的影响,因为刘海屏包含在状态栏中了;
(2)全屏显示的页面,主要适配的是:①启动页;②引导页;③阅读器,重点是阅读器。
android 兼容所有刘海屏的方案大全
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:design:28.0.0'
compile 'com.android.support:appcompat-v7:28.0.0'
compile 'com.android.support:support-v4:28.0.0'
}
此属性不仅可以针对Application生效,也可以对Activity配置生效。弊端:对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理,也不能针对某页面做特殊处理。
/**
* NotchManager.listenOnChange()
*
* 动态地增加/清除相关的flag,请求使用刘海区显示/不使用刘海区显示
*/
private static void listenOnChange(Activity activity) {
if (mNotchUsable) {
NotchJudgementUtil.addNotchFlag(activity);
} else {
NotchJudgementUtil.clearNotchFlag(activity);
}
}
(1) 在应用的AndroidManifest.xml中增加meta-data属性
(2)使用NotchManager类中的初始化方法:initNotchListener()调用listenOnChange()(initNotchListener()方法已实现);
public class BaseFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
// 初始化Android O,P刘海屏适配管理类
NotchManager.initNotchListener(this);
}
/**
* 因为刘海屏适配的问题,设置Activity全屏有且只可以使用此方法
*/
public void setFullScreen(boolean fullScreen) {
// 设置是否全屏
if (fullScreen) {
WindowManager.LayoutParams attr = getWindow().getAttributes();
attr.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setAttributes(attr);
} else {
final WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setAttributes(attrs);
}
// 必须在设置状态栏之后设置,使可以绘制到刘海区域
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
NotchJudgementUtil.setAndroidPDisplayCutoutMode(getWindow(), WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES);
// 设置SystemUi
if (fullScreen) {
View decorView = getWindow().getDecorView();
int systemUiVisibility = decorView.getSystemUiVisibility();
systemUiVisibility |= View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility);
} else {
View decorView = getWindow().getDecorView();
int systemUiVisibility = decorView.getSystemUiVisibility();
systemUiVisibility &= (~View.SYSTEM_UI_FLAG_FULLSCREEN);
systemUiVisibility &= (~View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
systemUiVisibility &= (~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility);
}
}
}
}
/**
* 在设置全屏的Activity中调用setFullScreen(true)
*/
public class SplashActivity extends BaseFragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 调用父类的setFullScreen方法设置是否全屏
setFullScreen(true);
}
}
参考链接:Android P 凹口屏支持,打造全面屏体验
参考链接:全面屏Splash/占位图适配/全面屏/刘海屏笔记
(1)在onCreate()设置全屏的Activity中调用setFullScreen(true)
public abstract class BaseReadActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// 设置全屏与否
setFullscreen();
}
}
(2)小书亭的方案:设置Android P的阅读器不扩展到刘海屏显示
public abstract class BaseReadActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
/设置全屏与否
setFullscreen();
}
}
private void setFullscreen() {
ReadConfig readConfig = UserSharedPreferences.getInstance().getReadConfig();
if (readConfig.isFullscreen()) {
// ......
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Android P的阅读器不扩展到刘海屏显示
NotchJudgementUtil.setAndroidPDisplayCutoutMode(getWindow(), WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER);
} else {
setFullScreen(true);
}
} else {
// ......
}
// Android O的某个厂商可以动态设置不扩展到刘海区显示
// Android O的华为手机阅读器不扩展到刘海屏显示
if (OSJudgementUtil.isHuaWei()) {
NotchJudgementUtil.clearNotchFlag(this);
}
}
(1)修改阅读器绘制顶部标题方法
protected void drawTopTitle(Canvas canvas, String title, IReader reader) {
int left = BaseApplication.getContext().getResources().getDimensionPixelOffset(R.dimen.marginleft_readview_top);
int height = BaseApplication.getContext().getResources().getDimensionPixelOffset(R.dimen.height_readview_top);
int[] notchSize = NotchManager.mNotchSize;
if (notchSize != null && notchSize[0] > 0) {
// 有刘海+可以拿到尺寸
int maxWidth = (canvas.getWidth() - notchSize[0] - left) / 2;
int maxLength = TextCustomUtils.breakText(title, reader.getTopInfoPaint(), maxWidth);
if (maxLength > 0 && maxLength < title.length()) {
title = title.substring(0, maxLength - 1) + "…";
}
} else {
if (title.length() >= 20) {
title = title.substring(0, 20) + "…";
}
}
float baseline = TextCustomUtils.getBaseline(height, reader.getBottomInfoPaint());
if (reader.isShowChapterName()) {
// 阅读设置---是否显示章节名
canvas.drawText(title, left, baseline, reader.getTopInfoPaint());
}
}
protected void drawTopTitle(Canvas canvas, String title, IReader reader) {
// ......
if (notchSize != null && notchSize[0] > 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.P
&& !(OSJudgementUtil.isXiaoMi() || OSJudgementUtil.isHuaWei() || OSJudgementUtil.isGoogle())) {
// 有刘海+可以拿到尺寸+低于Android P+需要计算刘海宽度的手机厂商
} else {
}
// ......
}
(1)将阅读器的内容绘制距离手机左边"状态栏高度"
private void setContentMarginLeft() {
if (NotchManager.getNotchAble() && mReadConfig.getScrrenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
mReadView.setContentMarginLeft((int) DeviceUtils.getStatusBarHeight(mActivity));
} else {
mReadView.setContentMarginLeft(0);
}
}
@Override
public int getContentMarginLeft() {
if (mPaddingLeft == 0) {
mPaddingLeft = getPaddingLeft();
}
return mPaddingLeft;
}
@Override
public void setContentMarginLeft(int left) {
mPaddingLeft = getPaddingLeft() + left;
}
(2)小书亭方案:将阅读器的内容绘制距离手机左边"状态栏高度"
private void setContentMarginLeft() {
if (NotchManager.isNeedPaddingLeft(mReadConfig.getScrrenOrientation())) {
mReadView.setContentMarginLeft((int) DeviceUtils.getStatusBarHeight(mActivity));
} else {
mReadView.setContentMarginLeft(0);
}
}
/**
* 小米/华为:旋转屏幕时动态的去清除或加上相关的flag
*
* @param orientation
*/
private void notchListenScreenOrientation(boolean orientation) {
if (NotchManager.isNeedNotchOrientation()) {
NotchManager.listenScreenOrientation(this, orientation);
}
}
public class NotchManager {
/**
* 是否有刘海
*/
private static boolean mHasNotch = false;
/**
* 判断刘海区域是否隐藏
*/
private static boolean mNotchUsable = true;
/**
* 计算获取刘海尺寸:width、height
*/
public static int[] mNotchSize = new int[2];
private static boolean isJudgeNotch = false;
/**
* 监听虚拟按键的初次判断及同一屏幕方向下的变化
*
* 结果存储在静态变量
*/
public static void initNotchListener(final Activity activity) {
if (ActivityUtils.assertActivityDestroyed(activity)) {
return;
}
if (isJudgeNotch) {
listenNotchOnChange(activity);
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
isJudgeNotch = true;
mHasNotch = false;
mNotchUsable = false;
mNotchSize = new int[2];
return;
}
NotchJudgementUtil.assertNotch(activity, new NotchJudgementUtil.NotchJudgementListener() {
@Override
public void end(boolean hasNotch, boolean notchUsable, int[] notchSize) {
isJudgeNotch = true;
mHasNotch = hasNotch;
mNotchUsable = notchUsable;
mNotchSize = notchSize;
if (mHasNotch) {
listenOnChange(activity);
}
}
});
}
/**
* true:有刘海、且刘海区域显示
*
* @return
*/
private static boolean getNotchAble() {
return mHasNotch && mNotchUsable;
}
/**
* true:有刘海
*
* @return
*/
private static boolean getHasNotch() {
return mHasNotch;
}
/**
* true:刘海区域显示
*
* @return
*/
private static boolean getNotchUsable() {
return mNotchUsable;
}
/**
* 监听到刘海区域是否隐藏发生变化。如果是,需要做如重新布局等相关操作
*
* @param activity
*/
private static void listenNotchOnChange(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !mHasNotch) {
return;
}
boolean notchUsable = NotchJudgementUtil.isNotchUsable(activity);
if (mNotchUsable != notchUsable) {
mNotchUsable = notchUsable;
}
listenOnChange(activity);
}
/**
* 动态地增加/清除相关的flag,请求扩展到刘海区显示/不扩展到刘海区显示
*
* @param activity
*/
private static void listenOnChange(Activity activity) {
if (mNotchUsable) {
NotchJudgementUtil.addNotchFlag(activity);
} else {
NotchJudgementUtil.clearNotchFlag(activity);
}
}
/**
* 旋转屏幕时动态的去清除或加上相关的flag
*
* @param activity
* @param orientation
*/
public static void listenScreenOrientation(Activity activity, boolean orientation) {
if (mNotchUsable && orientation) {
NotchJudgementUtil.addNotchFlag(activity);
} else {
NotchJudgementUtil.clearNotchFlag(activity);
}
}
}
以下代码不是必须,根据每个项目的UI需要添加:
public class NotchManager {
/**
* 判断是否是:小米
*/
public static boolean isNeedNotchOrientation() {
return OSJudgementUtil.isXiaoMi();
}
/**
* 判断是否:在竖屏下的总处理方法 --> 本地/在线阅读器
*
* @param orientation
* @return
*/
public static boolean isPaddingTop(int orientation) {
if (orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
return false;
}
// 默认Android P+竖屏不使用刘海区显示
if (isPPaddingTop()) {
// 28+的ViVo刘海屏一直扩展到刘海区
return OSJudgementUtil.isVIVO();
}
return isNeedPaddingTop(orientation) || isMiNeedPaddingTop() || isHuaNeedPaddingTop();
}
/**
* 判断是否在刘海屏下方绘制:在竖屏下 && 刘海屏可用 && 其他 ---> 网页阅读器
*
* @param orientation
* @return
*/
public static boolean isNeedPaddingTop(int orientation) {
if (orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
return false;
}
// 默认Android P+竖屏不扩展刘海区显示
if (isOsPPaddingTop()) {
// P+的ViVo刘海屏一直扩展到刘海区
return OSJudgementUtil.isVIVO();
}
// 非(华为/Google)-(因为华为/Google竖屏不使用刘海区显示)
return getNotchAble() && !(OSJudgementUtil.isHuaWei() || OSJudgementUtil.isGoogle());
}
/**
* 判断是否:有刘海屏 && Android P+
*
* @return
*/
private static boolean isOsPPaddingTop() {
return getHasNotch() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
/**
* 判断是否:有刘海屏 && 小米(小米隐藏刘海屏时,竖屏“本地/在线阅读器”的对话框按“存在刘海屏”显示,只区分是否有刘海屏)
*
* @return
*/
private static boolean isMiNeedPaddingTop() {
return getHasNotch() && OSJudgementUtil.isXiaoMi();
}
/**
* 判断是否:刘海屏是否可用 && 华为 && Android 8.1(华为隐藏刘海屏时,竖屏“本地/在线阅读器”的对话框按“存在刘海屏”显示,只区分刘海屏是否可用)
*
* @return
*/
private static boolean isHuaNeedPaddingTop() {
return getNotchUsable() && OSJudgementUtil.isHuaWei() && Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1;
}
/**
* 判断是否在刘海屏左方绘制:在横屏下 && 刘海屏可用 && 其他
*
* @param orientation
* @return
*/
public static boolean isNeedPaddingLeft(int orientation) {
if (orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
return false;
}
if (isPPaddingLeft()) {
// Android P+的ViVo刘海屏一直扩展到刘海区(Android P+其他机型默认不扩展到刘海区,不需要距离左边状态栏高度)
return OSJudgementUtil.isVIVO();
}
// 非(小米/华为/Google),因为小米/华为/Google横屏不扩展刘海区显示
return getNotchAble() && !(OSJudgementUtil.isXiaoMi() || OSJudgementUtil.isHuaWei() || OSJudgementUtil.isGoogle());
}
/**
* 判断是否:有刘海屏 && Android P+
*
* @return
*/
private static boolean isPPaddingLeft() {
return getHasNotch() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
}
@SuppressWarnings({"SimplifiableIfStatement", "ConstantConditions", "unused", "unchecked"})
public class NotchJudgementUtil {
public static void assertNotch(final Activity activity, final NotchJudgementListener listener) {
if (listener == null || ActivityUtils.assertActivityDestroyed(activity)) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (activity != null && activity.getWindow() != null) {
View decorView = activity.getWindow().getDecorView();
if (decorView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
decorView.getViewTreeObserver().addOnWindowAttachListener(new ViewTreeObserver.OnWindowAttachListener() {
@Override
public void onWindowAttached() {
listener.end(hasNotchInAndroidP(activity), true, getNotchSizeInAndroidP(activity));
}
@Override
public void onWindowDetached() {
}
});
return;
}
} else {
listener.end(false, false, new int[2]);
return;
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
listener.end(false, false, new int[2]);
return;
}
if (OSJudgementUtil.isHuaWei()) {
listener.end(hasNotchInHuaWei(activity), isNotchUsableInHuawei(activity), getNotchSizeInHuaWei(activity));
return;
}
if (OSJudgementUtil.isOPPO()) {
listener.end(hasNotchInOPPO(activity), isNotchUsableInOPPO(activity), getNotchSizeInOPPO(activity));
return;
}
if (OSJudgementUtil.isVIVO()) {
listener.end(hasNotchInVIVO(activity), isNotchUsableInVIVO(activity), getNotchSizeInVIVO(activity));
return;
}
if (OSJudgementUtil.isXiaoMi()) {
listener.end(hasNotchInXiaoMi(activity), isNotchUsableInXiaoMi(activity), getNotchSizeInXiaoMi(activity));
return;
}
listener.end(false, false, new int[2]);
}
/**
* int[0]值为刘海宽度 int[1]值为刘海高度
*/
private static int[] getNotchSizeInAndroidP(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (activity != null && activity.getWindow() != null) {
View decorView = activity.getWindow().getDecorView();
if (decorView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets windowInsets = decorView.getRootWindowInsets();
if (windowInsets != null && windowInsets.getDisplayCutout() != null) {
DisplayCutout cutout = windowInsets.getDisplayCutout();
return new int[]{DeviceUtils.getScreenWidthInPx(activity) - cutout.getSafeInsetLeft() - cutout.getSafeInsetRight(), cutout.getSafeInsetTop()};
}
}
}
}
return new int[2];
}
/**
* 因为小米获取不到刘海区的size,所以认为不可绘制
*
* @param context
* @return
*/
private static int[] getNotchSizeInXiaoMi(Context context) {
return new int[2];
}
/**
* VIVO获取刘海的尺寸
*
* @param context
* @return
*/
private static int[] getNotchSizeInVIVO(Context context) {
return new int[]{DeviceUtils.dp2px(context, 100), DeviceUtils.dp2px(context, 27)};
}
/**
* OPPO获取刘海的尺寸
*
* @param context
* @return
*/
private static int[] getNotchSizeInOPPO(Context context) {
return new int[]{324, 80};
}
/**
* 华为获取刘海的尺寸
*
* @param context
* @return
*/
private static int[] getNotchSizeInHuaWei(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");
}
return ret;
}
@SuppressWarnings("RedundantIfStatement")
private static boolean hasNotchInAndroidP(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
if (activity == null || activity.getWindow() == null || activity.getWindow().getDecorView() == null) {
return false;
}
if (OSJudgementUtil.isVIVO()) {
// AndroidP-VIVO的displayCutout为null,默认按有刘海区域处理
return true;
}
View decorView = activity.getWindow().getDecorView();
if (decorView != null && decorView.getRootWindowInsets() != null) {
DisplayCutout displayCutout = decorView.getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
List rectList = displayCutout.getBoundingRects();
if (rectList == null || rectList.size() == 0) {
return false;
} else {
return true;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
@SuppressWarnings("unchecked")
private static boolean hasNotchInVIVO(Context context) {
boolean ret = false;
try {
ClassLoader cl = context.getClassLoader();
@SuppressLint("PrivateApi") Class feature = cl.loadClass("android.util.FtFeature");
Method get = feature.getMethod("isFeatureSupport", int.class);
ret = (boolean) get.invoke(feature, 0x00000020);
} catch (ClassNotFoundException e) {
Log.e("test", "hasNotchInScreen ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "hasNotchInScreen NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "hasNotchInScreen Exception");
}
return ret;
}
private static boolean hasNotchInXiaoMi(Context context) {
return PropertyUtils.get("ro.miui.notch", "0").equalsIgnoreCase("1");
}
private static boolean hasNotchInOPPO(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
@SuppressWarnings("unchecked")
private static boolean hasNotchInHuaWei(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");
}
return ret;
}
/**
* 刘海区域是否可以隐藏的场景
*
* @param activity
* @return
*/
public static boolean isNotchUsable(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || ActivityUtils.assertActivityDestroyed(activity)) {
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return isNotchUsableInAndroidP(activity);
}
Context context = BaseApplication.getContext();
if (OSJudgementUtil.isHuaWei()) {
return isNotchUsableInHuawei(activity);
}
if (OSJudgementUtil.isOPPO()) {
return isNotchUsableInOPPO(activity);
}
if (OSJudgementUtil.isVIVO()) {
return isNotchUsableInVIVO(activity);
}
if (OSJudgementUtil.isXiaoMi()) {
return isNotchUsableInXiaoMi(activity);
}
return false;
}
/**
* 默认AndroidP+刘海屏不可用
*
* @param activity
* @return
*/
private static boolean isNotchUsableInAndroidP(Activity activity) {
return false;
}
/**
* vivo刘海屏可用
*
* @param activity
* @return
*/
private static boolean isNotchUsableInVIVO(Activity activity) {
return true;
}
/**
* oppo刘海屏可用
*
* @param activity
* @return
*/
private static boolean isNotchUsableInOPPO(Activity activity) {
return true;
}
/**
* 判断Huawei刘海屏是否可用
*
* @param activity
* @return
*/
private static boolean isNotchUsableInHuawei(Activity activity) {
// 0表示“默认”,1表示“隐藏显示区域”
return Settings.Secure.getInt(activity.getContentResolver(), "display_notch_status", 0) == 0;
}
/**
* 判断XiaoMi刘海屏是否可用
*
* @param activity
* @return
*/
private static boolean isNotchUsableInXiaoMi(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// 0表示“默认”,1表示“隐藏显示区域”
return Settings.Global.getInt(activity.getContentResolver(), "force_black", 0) == 0;
} else {
return false;
}
}
/**
* 在notchUsable=true时,使用刘海区显示(小米和华为动态配置)
*
* @param activity
*/
public static void addNotchFlag(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || ActivityUtils.assertActivityDestroyed(activity)) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return;
}
if (OSJudgementUtil.isHuaWei()) {
addNotchFlagInHuaWei(activity);
return;
}
if (OSJudgementUtil.isOPPO()) {
return;
}
if (OSJudgementUtil.isVIVO()) {
return;
}
if (OSJudgementUtil.isXiaoMi()) {
addNotchFlagInXiaoMi(activity);
return;
}
}
/**
* 在notchUsable=false时,不使用刘海区显示(小米和华为动态配置)
*
* @param activity
*/
public static void clearNotchFlag(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || ActivityUtils.assertActivityDestroyed(activity)) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return;
}
if (OSJudgementUtil.isHuaWei()) {
clearNotchFlagInHuaWei(activity);
return;
}
if (OSJudgementUtil.isOPPO()) {
return;
}
if (OSJudgementUtil.isVIVO()) {
return;
}
if (OSJudgementUtil.isXiaoMi()) {
clearNotchFlagInXiaoMi(activity);
}
}
/**
* Window级别的控制接口-华为不使用刘海区显示
*
* @param activity
*/
private static void clearNotchFlagInHuaWei(Activity activity) {
if (activity.getWindow() == null) {
return;
}
WindowManager.LayoutParams layoutParams = activity.getWindow().getAttributes();
try {
Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");
Constructor con = layoutParamsExCls.getConstructor(WindowManager.LayoutParams.class);
Object layoutParamsExObj = con.newInstance(layoutParams);
Method method = layoutParamsExCls.getMethod("clearHwFlags", int.class);
method.invoke(layoutParamsExObj, 0x00010000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Window级别的控制接口-华为使用刘海区显示
*
* @param activity
*/
private static void addNotchFlagInHuaWei(Activity activity) {
if (activity.getWindow() == null) {
return;
}
WindowManager.LayoutParams layoutParams = activity.getWindow().getAttributes();
try {
Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");
Constructor con = layoutParamsExCls.getConstructor(WindowManager.LayoutParams.class);
Object layoutParamsExObj = con.newInstance(layoutParams);
Method method = layoutParamsExCls.getMethod("addHwFlags",
int.class);
method.invoke(layoutParamsExObj, 0x00010000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Window级别的控制接口-小米不使用刘海区显示
*
* @param activity
*/
private static void clearNotchFlagInXiaoMi(Activity activity) {
if (activity.getWindow() == null) {
return;
}
// 横竖屏都绘制到耳朵区
int flag = 0x00000100 | 0x00000200 | 0x00000400;
try {
Method method = Window.class.getMethod("clearExtraFlags", int.class);
method.invoke(activity.getWindow(), flag);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Window级别的控制接口-小米使用刘海区显示
*
* @param activity
*/
private static void addNotchFlagInXiaoMi(Activity activity) {
if (activity.getWindow() == null) {
return;
}
// 横竖屏都绘制到耳朵区
int flag = 0x00000100 | 0x00000200 | 0x00000400;
try {
Method method = Window.class.getMethod("addExtraFlags", int.class);
method.invoke(activity.getWindow(), flag);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Window级别的控制接口-Android P动态控制是否使用刘海区显示
*
* 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
*
* @param window
*/
public static void setAndroidPDisplayCutoutMode(Window window, int cutoutMode) {
if (window == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return;
}
WindowManager.LayoutParams windowManagerDu = window.getAttributes();
windowManagerDu.layoutInDisplayCutoutMode =cutoutMode;
window.setAttributes(windowManagerDu );
}
public interface NotchJudgementListener {
/**
* 刘海信息回调
*
* @param hasNotch
* @param notchUsable
* @param notchSize
*/
void end(boolean hasNotch, boolean notchUsable, int[] notchSize);
}
}
(1)系统判断OSJudgementUtil
public final class OSJudgementUtil {
public static boolean isHuaWei() {
return "HUAWEI".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isZTE() {
return "ZTE".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean is360() {
return "360".equalsIgnoreCase(android.os.Build.MANUFACTURER) || "QIKU".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isMeizu() {
return "Meizu".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isOPPO() {
return "OPPO".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isVIVO() {
return "VIVO".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isXiaoMi() {
return "Xiaomi".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
public static boolean isGoogle() {
return "Google".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
}
(2) 设备相关信息DeviceUtils
public class DeviceUtils {
/**
* 获取屏幕宽度(单位:像素)
*
* @param context
* @return
*/
public static int getScreenWidthInPx(Context context) {
if (context == null) {
return 0;
}
return context.getResources().getDisplayMetrics().widthPixels;
}
/**
* dp转px
*
* @param context
* @param dp
* @return
*/
public static int dp2px(Context context, int dp) {
if (context == null) {
return 0;
}
float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
(3) ActivityUtils
public class ActivityUtils {
/**
* @param activity
* @return true=Activity已经销毁,false=没有销毁
*/
public static boolean assertActivityDestroyed(Activity activity) {
if (activity == null) {
return true;
}
WeakReference weakReference = new WeakReference<>(activity);
Activity weakActivity = weakReference.get();
if (weakActivity == null) {
return true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && weakActivity.isDestroyed()) {
return true;
}
if (weakActivity.isFinishing()) {
return true;
}
return false;
}
}
(4) PropertyUtils
public class PropertyUtils {
private static volatile Method set = null;
private static volatile Method get = null;
public static void set(String prop, String value) {
try {
if (null == set) {
synchronized (PropertyUtils.class) {
if (null == set) {
Class> cls = Class.forName("android.os.SystemProperties");
set = cls.getDeclaredMethod("set", new Class>[]{String.class, String.class});
}
}
}
set.invoke(null, new Object[]{prop, value});
} catch (Throwable e) {
e.printStackTrace();
}
}
public static String get(String prop, String defaultvalue) {
String value = defaultvalue;
try {
if (null == get) {
synchronized (PropertyUtils.class) {
if (null == get) {
Class> cls = Class.forName("android.os.SystemProperties");
get = cls.getDeclaredMethod("get", new Class>[]{String.class, String.class});
}
}
}
value = (String) (get.invoke(null, new Object[]{prop, defaultvalue}));
} catch (Throwable e) {
e.printStackTrace();
}
return value;
}
}