前言
劉海屏是指某些設備顯示屏上的一個區(qū)域延伸到顯示面,這樣既能為用戶提供全面屏體驗,又能為設備正面的重要傳感器留出空間。Android在搭載Android 9.0/P(API 級別 28)及更高版本的設備上正式支持劉海屏缔刹。設備制造商也可以選擇在搭載Android 8.1或更低版本的設備上支持劉海屏。
官方地址:
https://developer.android.com/guide/topics/display-cutout
Android 9.0及以上:
layoutInDisplayCutoutMode
Android允許控制是否在劉海區(qū)域內(nèi)顯示內(nèi)容魄梯。窗口布局屬性layoutInDisplayCutoutMode控制內(nèi)容如何呈現(xiàn)在劉海區(qū)域中:
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:這是默認行為桨螺,如上所述。在豎屏模式下酿秸,內(nèi)容會呈現(xiàn)到劉海區(qū)域中灭翔;但在橫屏模式下,內(nèi)容會顯示黑邊辣苏。
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:在豎屏模式和橫屏模式下肝箱,內(nèi)容都會呈現(xiàn)到劉海區(qū)域中。
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:內(nèi)容從不呈現(xiàn)到劉海區(qū)域中稀蟋。
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:內(nèi)容始終呈現(xiàn)到劉海區(qū)域中煌张。
DisplayCutout
- getBoundingRects():返回Rects的列表,每個Rects都是顯示屏上非功能區(qū)域的邊界矩形退客。
- getSafeInsetLeft():返回安全區(qū)域距離屏幕左邊的距離专控,單位是px。
- getSafeInsetRight():返回安全區(qū)域距離屏幕右邊的距離兜叨,單位是px躏率。
- getSafeInsetTop():返回安全區(qū)域距離屏幕頂部的距離,單位是px茫藏。
- getSafeInsetBottom():返回安全區(qū)域距離屏幕底部的距離误趴,單位是px。
通過代碼演示不同窗口及l(fā)ayoutInDisplayCutoutMode的區(qū)別:
主題
將主題修改成不帶ActionBar的樣式
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.PBangScreenDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
XXXXX
</style>
</resources>
布局
將文本頂在布局的左上角务傲,方便演示
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_dark"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
代碼
檢測是否是劉海屏凉当,如果存在劉海屏枣申,則給布局增加一個PaddingTop
@RequiresApi(api = Build.VERSION_CODES.P)
private void detect() {
final View decorView = getWindow().getDecorView();
decorView.post(new Runnable() {
@Override
public void run() {
WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
if (rootWindowInsets == null) {
return;
}
DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
if (displayCutout == null) {
return;
}
Log.e("yzt", "安全區(qū)域距離屏幕左邊的距離>>>" + displayCutout.getSafeInsetLeft());
Log.e("yzt", "安全區(qū)域距離屏幕右部的距離>>>" + displayCutout.getSafeInsetRight());
Log.e("yzt", "安全區(qū)域距離屏幕頂部的距離>>>" + displayCutout.getSafeInsetTop());
Log.e("yzt", "安全區(qū)域距離屏幕底部的距離>>>" + displayCutout.getSafeInsetBottom());
List<Rect> rects = displayCutout.getBoundingRects();
if (rects == null || rects.size() == 0) {
Log.e("yzt", "不是劉海屏");
} else {
Log.e("yzt", "劉海屏數(shù)量>>>" + rects.size());
for (Rect rect : rects) {
Log.e("yzt", "劉海屏區(qū)域>>>" + rect);
}
layout.setPadding(0, displayCutout.getSafeInsetTop(), 0, 0);
}
}
});
}
全屏情況
設置全屏,寫在setContentView()之前
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
設置layoutInDisplayCutoutMode
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_XXXXX;
getWindow().setAttributes(lp);
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS模式
透明狀態(tài)欄情況
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER模式
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS模式
可以看出當劉海區(qū)域完全在系統(tǒng)的狀態(tài)欄時看杭,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT的顯示效果與LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES一致忠藤。
完整代碼
public class MainActivity extends AppCompatActivity {
private View layout;
@RequiresApi(api = Build.VERSION_CODES.P)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
setContentView(R.layout.activity_main);
WindowManager.LayoutParams lp = getWindow().getAttributes();
//在豎屏模式下,內(nèi)容會呈現(xiàn)到劉海區(qū)域中泊窘;但在橫屏模式下熄驼,內(nèi)容會顯示黑邊。
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
//在豎屏模式和橫屏模式下烘豹,內(nèi)容都會呈現(xiàn)到劉海區(qū)域中瓜贾。
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
//內(nèi)容從不呈現(xiàn)到劉海區(qū)域中。
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
//內(nèi)容始終呈現(xiàn)到劉海區(qū)域中携悯。
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
getWindow().setAttributes(lp);
layout = findViewById(R.id.layout);
detect();
}
@RequiresApi(api = Build.VERSION_CODES.P)
private void detect() {
final View decorView = getWindow().getDecorView();
decorView.post(new Runnable() {
@Override
public void run() {
WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
if (rootWindowInsets == null) {
return;
}
DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
if (displayCutout == null) {
return;
}
Log.e("yzt", "安全區(qū)域距離屏幕左邊的距離>>>" + displayCutout.getSafeInsetLeft());
Log.e("yzt", "安全區(qū)域距離屏幕右部的距離>>>" + displayCutout.getSafeInsetRight());
Log.e("yzt", "安全區(qū)域距離屏幕頂部的距離>>>" + displayCutout.getSafeInsetTop());
Log.e("yzt", "安全區(qū)域距離屏幕底部的距離>>>" + displayCutout.getSafeInsetBottom());
List<Rect> rects = displayCutout.getBoundingRects();
if (rects == null || rects.size() == 0) {
Log.e("yzt", "不是劉海屏");
} else {
Log.e("yzt", "劉海屏數(shù)量>>>" + rects.size());
for (Rect rect : rects) {
Log.e("yzt", "劉海屏區(qū)域>>>" + rect);
}
layout.setPadding(0, displayCutout.getSafeInsetTop(), 0, 0);
}
}
});
}
}
Android 9.0以下
上面是Android P才有的解決方案祭芦。然而國產(chǎn)廠商在Android P之前(基本都是Android O)就用上了高檔大氣上檔次的劉海屏,所以憔鬼,這也造就了各大廠商在Android P之前的解決方案百花齊放龟劲。這里不詳細介紹。
結(jié)尾
核心思路就是判斷是否帶有劉海屏轴或,根據(jù)自己的項目判斷窗口的情況昌跌,再去決定是否需要做適配,如果需要適配再去根據(jù)實際的設計要求做適配照雁。