劉海屏之全面屏適配攻略
阿新 • • 發佈:2018-12-19
劉海屏之全面屏攻略
前言
由於蘋果公司的“先進設計”導致各大手機廠商紛紛跟風其設計,導致Android的螢幕適配出現新的剛需——劉海屏的適配。為了簡化這些適配操作以及繁瑣的判斷封裝優化出一個工具庫:BangScreenToolsMaster
適配方案及原理
適配流程
AndroidP的適配方式:
AndroidP及以上的方式必須適用於sdk大於等於28的情況下使用。
顯示模式: Android P中新增了一個佈局引數屬性 |
|
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
<meta-data android:name="android.notch_support" android:value="true"/>
- 對
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<Rect> getBangSize(Window window) {
ArrayList<Rect> result = new ArrayList<Rect>();
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<Rect> getBangSize(Window window) {
List<Rect> 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<Rect> getBangSize(Window window) {
List<Rect> 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<Rect> getBangSize(Window window) {
List<Rect> 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;
}
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的介紹接下來展示下封裝後代碼執行的效果