做者 : 快樂丸 轉載自 :www.jianshu.com/p/8ead0701d…android
目前市面上的劉海屏和水滴屏手機愈來愈多了,顏值方面是因人而異,有的人以爲很好看,也有人以爲醜爆了,我我的以爲是還能夠。可是做爲移動開發者來講,這並非一件好事,愈來愈多異形屏手機的出現意味着咱們須要投入大量精力在適配上(就不提以後會出的摺疊屏手機了)。本文總結了當下主流手機的劉海屏適配方案,鑑於目前Android碎片化的狀況,想要覆蓋全部的機型是不可能的,可是能適配一些是一些,總比什麼都不作要好。git
所謂劉海屏,指的是手機屏幕正上方因爲追求極致邊框而採用的一種手機解決方案。因形似劉海兒而得名——來自百度百科,水滴屏也是相似,爲了簡單起見,下文就統稱這兩種爲劉海屏了。github
這裏先上一張官方的圖api
從圖中能夠看出,劉海區域是鑲嵌在狀態欄內部的,劉海區域的高度通常是不超過狀態欄高度的。所以,當咱們的應用佈局須要佔據狀態欄來顯示時,就須要考慮到劉海區域是否會遮擋住頁面上的控件或者背景,這就是爲何將狀態欄區域稱爲危險區域。若是應用不須要佔據狀態欄顯示,所有顯示在安全區域內,那麼恭喜你,不須要作任何適配處理。總結來講,只有當應用須要全屏顯示時才須要進行適配。 全屏顯示無非就是兩種狀況:第一種是咱們常說的沉浸式狀態欄,也就是狀態欄透明,頁面的佈局延伸到狀態欄顯示,這種狀況下狀態欄依然可見;第二種是相似應用的閃屏頁風格,頁面全屏顯示,狀態欄不可見。這兩種狀況下若是不進行適配處理都會產生一些問題。 先來看第一種狀況,沉浸式風格。須要將狀態欄設置爲透明,須要注意只有在Android 4.4(API Level 19)以上才支持設置透明狀態欄。有兩種設置方法: 方法一:爲Activity設置style,添加一個屬性:安全
<item name="android:windowTranslucentStatus">true</item>
複製代碼
方法二:在Activity的onCreate()中爲Window添加Flagbash
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明狀態欄
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
複製代碼
頁面的佈局很簡單,只包含一個按鈕,爲了明顯,我爲根佈局設置了一個背景。 activity_immersive.xmlapp
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical">
<Button
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
複製代碼
運行以後發現按鈕會被劉海區域所遮擋,如圖所示:ide
再說第二種狀況,全屏風格,狀態欄不可見。一樣有兩種設置方法: 方法一:爲Activity設置style,添加屬性:工具
<item name="android:windowFullscreen">true</item>
<!-- 這裏爲了簡單,直接從style中指定一個背景 -->
<item name="android:windowBackground">@mipmap/bg</item>
複製代碼
方法二:在Activity的OnCreate()中添加代碼:佈局
public class FullScreenActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全屏顯示
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
複製代碼
補充說明一點,如今的手機屏幕高寬比例愈來愈大,咱們還須要額外作一下適配才能使應用在全部手機上都能全屏顯示,具體方式有兩種: 方式一:在AndroidManifest.xml中配置支持最大高寬比
<meta-data android:name="android.max_aspect"
android:value="ratio_float" />
複製代碼
或者
android:maxAspectRatio="ratio_float" (API LEVEL 26)
複製代碼
說明:以上兩種接口能夠二選一,ratio_float = 屏幕高 / 屏幕寬 (如oppo新機型屏幕分辨率爲2280 x 1080, ratio_float = 2280 / 1080 = 2.11,建議設置 ratio_float爲2.2或者更大) 方式二:在AndroidManifest.xml中配置支持分屏,注意驗證分屏下界面兼容性
android:resizeableActivity="true"
複製代碼
也能夠經過設置targetSdkVersion>=24(即Android 7.0),該屬性的值會默認爲true,就不須要在AndroidManifest.xml中配置了。 運行以後,咱們發現狀態欄的部分留出了一條黑邊,看上起很奇怪,這顯然不是咱們想要的效果。
上文中已經展現了劉海屏中全屏顯示帶來的問題,那麼如何去解決呢?
其實沉浸式狀態欄帶來的遮擋問題與劉海屏無關,本質上是因爲設置了透明狀態欄致使佈局延伸到了狀態欄中,就算是不具備劉海屏,必定程度上也會形成佈局的遮擋。不過既然劉海屏是處在狀態欄當中的,那麼咱們就把這種狀況也包含在劉海屏的適配中。清楚了緣由以後,解決起來就很簡單了,咱們只須要讓控件或佈局避開狀態欄顯示就能夠了,具體的解決方法有三種。 方法一.利用fitsSystemWindows屬性 當咱們給最外層View設置了android:fitsSystemWindows="true"屬性後,當設置了透明狀態欄或者透明導航欄後,就會自動給View添加paddingTop或paddingBottom屬性,這樣就在屏幕上預留出了狀態欄的高度,咱們的佈局就不會佔用狀態欄來顯示了。 activity_immersive.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:fitsSystemWindows="true"
android:orientation="vertical">
<Button
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
複製代碼
方法二.根據狀態欄高度手動設置paddingTop 這種方法的實現本質上和設置fitsSystemWindows是同樣的,首先獲取狀態欄高度,而後設置根佈局的paddingTop等於狀態欄高度就能夠了,代碼以下:
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明狀態欄
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
LinearLayout llRoot = findViewById(R.id.ll_root);
// 設置根佈局的paddingTop
llRoot.setPadding(0, getStatusBarHeight(this), 0, 0);
}
/**
* 獲取狀態欄高度
*
* @param context
* @return
*/
public int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
}
複製代碼
方法三.在佈局中添加一個和狀態欄高度相同的View 和前兩種方法原理相似,一樣是讓屏幕預留出狀態欄的高度,這裏在根佈局中添加了一個透明的View,高度和狀態欄高度相同。這種方法的好處是能夠自定義填充狀態欄View的背景,更靈活地實現咱們想要的效果。
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明狀態欄
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
LinearLayout llRoot = findViewById(R.id.ll_root);
View statusBarView = new View(this);
statusBarView.setBackgroundColor(Color.TRANSPARENT);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(this));
// 在根佈局中添加一個狀態欄高度的View
llRoot.addView(statusBarView, 0, lp);
}
/**
* 獲取狀態欄高度
*
* @param context
* @return
*/
public int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
}
複製代碼
適配以後成功地將控件避開了狀態欄(危險區域),以下圖所示:
對於全屏顯示的狀況,處理起來要相對麻煩一些,下面重點說一下這種狀況下的適配方案。
谷歌官方從Android P開始給開發者提供了劉海屏相關的API,能夠經過直接調用API來進行劉海屏的適配處理。 經過DisplayCutout類能夠得到安全區域的範圍以及劉海區域(官方的叫法是缺口)的信息,須要注意只有API Level在28及以上才能夠調用。
/**
* 得到劉海區域信息
*/
@TargetApi(28)
public void getNotchParams() {
final View decorView = getWindow().getDecorView();
if (decorView != null) {
decorView.post(new Runnable() {
@Override
public void run() {
WindowInsets windowInsets = decorView.getRootWindowInsets();
if (windowInsets != null) {
// 當全屏頂部顯示黑邊時,getDisplayCutout()返回爲null
DisplayCutout displayCutout = windowInsets.getDisplayCutout();
Log.e("TAG", "安全區域距離屏幕左邊的距離 SafeInsetLeft:" + displayCutout.getSafeInsetLeft());
Log.e("TAG", "安全區域距離屏幕右部的距離 SafeInsetRight:" + displayCutout.getSafeInsetRight());
Log.e("TAG", "安全區域距離屏幕頂部的距離 SafeInsetTop:" + displayCutout.getSafeInsetTop());
Log.e("TAG", "安全區域距離屏幕底部的距離 SafeInsetBottom:" + displayCutout.getSafeInsetBottom());
// 得到劉海區域
List<Rect> rects = displayCutout.getBoundingRects();
if (rects == null || rects.size() == 0) {
Log.e("TAG", "不是劉海屏");
} else {
Log.e("TAG", "劉海屏數量:" + rects.size());
for (Rect rect : rects) {
Log.e("TAG", "劉海屏區域:" + rect);
}
}
}
}
});
}
}
複製代碼
這裏我在測試時也發現了一個問題,就是若是是在style中設置了全屏模式,在適配以前,頂部狀態欄區域顯示一條黑邊,這時候調用getDisplayCutout()獲取DisplayCutout對象返回的結果是null,其實這也不難理解,由於這時候是看不出劉海區域的,可是這樣會致使在適配以前沒法經過DisplayCutout判斷是否存在劉海屏,只能在適配後才能獲取到劉海區域信息,所以只能對於全部設備都添加適配代碼。 那麼接下來如何進行適配呢,Android P中增長了一個窗口布局參數屬性layoutInDisplayCutoutMode,該屬性有三個值能夠取:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默認的佈局模式,僅當劉海區域徹底包含在狀態欄之中時,才容許窗口延伸到劉海區域顯示,也就是說,若是沒有設置爲全屏顯示模式,就容許窗口延伸到劉海區域,不然不容許。 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:永遠不容許窗口延伸到劉海區域。 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:始終容許窗口延伸到屏幕短邊上的劉海區域,窗口永遠不會延伸到屏幕長邊上的劉海區域。
還有一個LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS模式,目前已經被LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES所取代,不容許使用了,這裏就不提了。 這麼看可能仍是有些不理解,接下來咱們在一個全屏顯示的頁面分別設置三種佈局模式,看看有什麼區別。
public class FullScreenActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 僅當缺口區域徹底包含在狀態欄之中時,才容許窗口延伸到劉海區域顯示
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
// 永遠不容許窗口延伸到劉海區域
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
// 始終容許窗口延伸到屏幕短邊上的劉海區域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
}
}
}
複製代碼
三種模式下的顯示效果以下圖所示:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
能夠看出,當在全屏顯示狀況下,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT和LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的效果是同樣的,都是在狀態欄顯示一條黑邊,也就是不容許窗口布局延伸到劉海區域,而LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES則容許窗口布局延伸到了劉海區域,這裏須要注意是短邊劉海區域,不過通常市面上的手機劉海區域都是在短邊上的,我是沒見過劉海長在「腰」上的,所以利用這個模式就實現適配了。 經過以前沉浸式狀態欄的顯示效果能夠看出,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT在此時是容許窗口布局延伸到劉海區域的,所以更證明了只有在全屏顯示的狀況下該模式纔不容許窗口布局延伸到劉海區域。 適配後效果以下,如今看起來就很舒服了:
我這裏爲了簡單沒有添加任何控件,實際開發中在全屏顯示後咱們仍然須要考慮劉海區域是否會遮擋顯示的內容和控件,一樣須要避開危險區域來顯示。作法和沉浸式狀態欄的適配相同,原理一樣是將佈局下移,預留出狀態欄的高度,這裏就不一一列舉了。
目前市面上的劉海屏手機能夠說是琳琅滿目,各大廠商都在追求極致的屏佔比,推出的新機型也基本上都有劉海屏,針對Android P如下的手機,咱們只能依照各個廠商提供的適配方案來進行適配。我也查閱了網上的一些適配文章,主要仍是針對目前主流的手機品牌,本文總結了華爲、小米、Vivo和Oppo的適配方案,其餘品牌的手機以後有時間的話可能會再考慮。
華爲官方提供的適配文檔:華爲劉海屏手機安卓O版本適配指導 文檔中提供了不少劉海屏相關的方法,這裏就不一一列舉了,着重看一下咱們須要用到的方法。 判斷是否有劉海屏
/**
* 判斷是否有劉海屏
*
* @param context
* @return true:有劉海屏;false:沒有劉海屏
*/
public static boolean hasNotch(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;
}
}
複製代碼
應用頁面設置使用劉海區顯示 官方提供了兩種適配方案: 方案一.使用新增的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:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="android.notch_support"
android:value="true" />
...
</application>
複製代碼
也能夠針對指定的Activity添加,意味着能夠針對單個頁面進行劉海屏適配,設置了該屬性的Activity系統將不會作特殊處理。
<!-- 全屏顯示頁面 -->
<activity
android:name=".ui.FullScreenActivity"
android:screenOrientation="portrait"
android:theme="@style/FullScreenTheme">
<meta-data
android:name="android.notch_support"
android:value="true" />
</activity>
複製代碼
方案二.使用給window添加新增的FLAG_NOTCH_SUPPORT 代碼以下:
/**
* 設置應用窗口在劉海屏手機使用劉海區
* <p>
* 經過添加窗口FLAG的方式設置頁面使用劉海區顯示
*
* @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(WindowManager.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");
}
}
複製代碼
官方提供的全部方法我已經放到了工具類HwNotchUtils裏,能夠根據需求來使用。
小米官方提供的適配文檔:dev.mi.com/console/doc…
咱們一樣看一下關鍵方法。 判斷是否有劉海屏
/**
* 判斷是否有劉海屏
*
* @param context
* @return true:有劉海屏;false:沒有劉海屏
*/
public static boolean hasNotch(Context context) {
boolean ret = false;
try {
ClassLoader cl = context.getClassLoader();
Class SystemProperties = cl.loadClass("android.os.SystemProperties");
Method get = SystemProperties.getMethod("getInt", String.class, int.class);
ret = (Integer) get.invoke(SystemProperties, "ro.miui.notch", 0) == 1;
} catch (Exception e) {
e.printStackTrace();
} finally {
return ret;
}
}
複製代碼
應用頁面設置使用劉海區顯示 小米提供的適配方案一樣有兩種(meta-data和Flag),使用方法和華爲相似。 方案一.Application級別的控制接口 在 Application 下增長一個 meta-data,用以聲明該應用窗口是否能夠延伸到狀態欄。
<meta-data
android:name="notch.config"
android:value="portrait|landscape"/>
複製代碼
其中,value的值能夠是如下四種
"none" 橫豎屏都不繪製耳朵區
"portrait" 豎屏繪製到耳朵區
"landscape" 橫屏繪製到耳朵區
"portrait|landscape" 橫豎屏都繪製到耳朵區
複製代碼
這裏的耳朵區指的就是劉海區兩側的狀態欄區域
雖然官方文檔上說的是Application級別的,可是我以爲也能夠針對某一個Activity來配置,不過因爲手頭上的手機條件不知足,我並無驗證,若是有小夥伴測試過的話能夠反饋一下,我再修正一下這裏的說法。 方案二.Window級別的控制接口 經過給Window添加Flag也能夠實現將窗口布局延伸到狀態欄中顯示。
/*劉海屏全屏顯示FLAG*/
public static final int FLAG_NOTCH_SUPPORT = 0x00000100; // 開啓配置
public static final int FLAG_NOTCH_PORTRAIT = 0x00000200; // 豎屏配置
public static final int FLAG_NOTCH_HORIZONTAL = 0x00000400; // 橫屏配置
/**
* 設置應用窗口在劉海屏手機使用劉海區
* <p>
* 經過添加窗口FLAG的方式設置頁面使用劉海區顯示
*
* @param window 應用頁面window對象
*/
public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
// 豎屏繪製到耳朵區
int flag = FLAG_NOTCH_SUPPORT | FLAG_NOTCH_PORTRAIT;
try {
Method method = Window.class.getMethod("addExtraFlags",
int.class);
method.invoke(window, flag);
} catch (Exception e) {
Log.e("test", "addExtraFlags not found.");
}
}
複製代碼
官方提供的全部方法我已經放到了工具類XiaomiNotchUtils裏,能夠根據需求來使用。 這裏說一下個人測試狀況,我是用小米8測試的,系統版本已經升到了Android P,利用小米官方提供的適配方法沒有效果,只能用谷歌官方針對Android P的適配方案,這一點小米的官方文檔也提到了。
至於Android P如下版本的小米手機,我並無測試,若是有哪位大佬測試過了發現有問題能夠反饋一下。
Vivo官方提供的適配文檔:Vivo全面屏應用適配指南 Oppo官方提供的適配文檔:Oppo凹形屏適配指南 這裏把Vivo和Oppo放在一塊兒說,官方提供的資料不像華爲和小米那麼詳細,只是提供了判斷是否有劉海屏的方法。 Vivo判斷是否有劉海屏
public static final int VIVO_NOTCH = 0x00000020; // 是否有劉海
public static final int VIVO_FILLET = 0x00000008; // 是否有圓角
/**
* 判斷是否有劉海屏
*
* @param context
* @return true:有劉海屏;false:沒有劉海屏
*/
public static boolean hasNotch(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", "hasNotchAtVivo ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("Notch", "hasNotchAtVivo NoSuchMethodException");
} catch (Exception e) {
Log.e("Notch", "hasNotchAtVivo Exception");
} finally {
return ret;
}
}
複製代碼
Oppo判斷是否有劉海屏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Android P利用官方提供的API適配
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 始終容許窗口延伸到屏幕短邊上的缺口區域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
} else {
// Android P如下根據手機廠商的適配方案進行適配
if (RomUtils.isHuawei() && HwNotchUtils.hasNotch(this)) {
HwNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
} else if (RomUtils.isXiaomi() && XiaomiNotchUtils.hasNotch(this)) {
XiaomiNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
}
}
}
複製代碼
至於全屏顯示的適配方案,經過閱讀官方文檔和網上的其餘適配文章,我我的總結一下就是這兩種品牌的手機在設置全屏顯示時都無需作任何處理(前提是適配了全面屏,上文中提到過如何配置),也就是不會產生黑邊,咱們只須要避免佈局中的內容或控件不被劉海區域所遮擋就能夠了。具體的作法和沉浸式狀態欄的適配相同,基本原理仍是將窗口布局下移,預留出狀態欄的高度。 注:因爲手頭沒有這兩種廠商的手機,所以並無驗證,這一點確實是我作得不夠嚴謹,有好心的大佬驗證以後歡迎指正。 其實我原本也想列出魅族的適配方案的,可是實在是沒找到官方文檔。。。若是有知道的大佬能夠提供一下,我後面會把適配方案補上。 適配時的基本邏輯就是先判斷手機的品牌,這裏我利用了一個開源工具類項目AndroidUtilCode,提供了一個獲取手機Rom信息的工具類RomUtils,用起來很方便,而後判斷是不是劉海屏,針對劉海屏手機添加適配代碼。完整的適配代碼以下所示:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Android P利用官方提供的API適配
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 始終容許窗口延伸到屏幕短邊上的缺口區域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
} else {
// Android P如下根據手機廠商的適配方案進行適配
if (RomUtils.isHuawei() && HwNotchUtils.hasNotch(this)) {
HwNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
} else if (RomUtils.isXiaomi() && XiaomiNotchUtils.hasNotch(this)) {
XiaomiNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
}
}
複製代碼
雖然文中介紹了不少適配的內容,但其實在開發中須要咱們適配劉海屏的狀況並很少,只有兩種狀況須要咱們進行考慮: 1.沉浸式狀態欄,窗口布局延伸到了狀態欄中,是否會遮擋必要的內容或控件(處在危險區域)。適配方案就是將窗口布局下移,預留出狀態欄的空間。 2.全屏顯示模式,不作適配的話狀態欄會呈現一條黑邊。適配方案是首先判斷系統版本,是Android P及以上就按照官方的API來適配,不然根據手機廠商的適配方案進行適配。鑑於目前市面上Android P尚未普及,爲了帶來更好的用戶體驗,咱們仍是須要多花一些精力來適配各個手機廠商的劉海屏手機。 最後提示一下,本文只列出了四個當下主流手機廠商的適配方案,我本身驗證過的只有華爲和小米(只驗證了Android P)的方案,對於Vivo和Oppo的一些結論我可能說得不對,歡迎你們指正。固然,若是你們還須要其餘廠商的適配方案,也歡迎提出,我會盡力補上。 相關的代碼和工具類我已經上傳到了github,能夠下載Demo來查看,你們一塊兒交流