刘海屏适配,就是不让刘海遮挡住应用程序,不影响应用程序的正常使用。
竖屏情况下,系统会根据刘海的高度,响应调整状态栏的高度。因此,刘海对竖屏模式没有影响。
可是横屏模式下,刘海区域就会变成一条大黑边。
Android 9.0系统中提供了3种layoutInDisplayCutoutMode属性来允许应用自主决定该如何对刘海屏设备进行适配:
①LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默认的属性,应用程序的内容在竖屏模式下自动延伸到刘海区域,横屏模式下不会延伸到刘海区域。
②LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:不管横屏还是竖屏模式,都允许应用程序的内容延伸到刘海区域。
③LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:永远不允许应用程序的内容延伸到刘海区域。
这种情况下,就需要指定layoutInDisplayCutoutMode属性的值,让我们的应用程序具备更好的用户体验。
layoutInDisplayCutoutMode属性可以在xml中指定,创建values-v28文件夹,创建styles.xml文件:
<style name="ActivityTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">
shortEdges <!-- 可选项 default, shortEdges, never -->
</item>
</style>
layoutInDisplayCutoutMode属性也可以在代码中指定:
if (Build.VERSION.SDK_INT >= 28) {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(params);
}
那么问题来了,假如有一个Button正好位于刘海屏的位置,那么就得在设计上进行适配了。
竖屏模式下,顶部View被刘海屏遮挡:
横屏模式下,侧边View也会被遮挡:
Android在9.0系统提供了一套获取安全显示区域的API,只需确认哪些位置有可能被遮挡,并相应的对遮挡位置进行偏移即可:
if (Build.VERSION.SDK_INT >= 28) {
rootLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
DisplayCutout displayCutout = windowInsets.getDisplayCutout();
if (displayCutout != null) {
int left = displayCutout.getSafeInsetLeft();
int top = displayCutout.getSafeInsetTop();
int right = displayCutout.getSafeInsetRight();
int bottom = displayCutout.getSafeInsetBottom();
}
return windowInsets.consumeSystemWindowInsets();
}
});
}
通过Android9.0提供的获取安全显示区域的API,计算出左上右下的安全距离,再对目标View设置margin间距:
可以看出,我们的控件已经根据计算出的安全距离进行了适当的位置偏移。同时需要注意一点,横屏时,侧边View进行了安全偏移,但是顶部View并未显示在正中。因为DisplayCount让我们所有的View都进行了安全偏移。针对这种“BUG”,只能加判断逻辑,被遮挡的View再去安全偏移,不受影响的View不用管了。
完整代码如下:
activity_cutout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/husky"
tools:context=".cutout.CutoutActivity">
<Button
android:id="@+id/top"
android:text="顶部可交互控件"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/side"
android:text="侧边可交互控件"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
CutoutActivity.java
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.widget.Button;
import android.widget.FrameLayout;
import com.example.husky.xiangxue.R;
/**
* 刘海屏适配
*/
public class CutoutActivity extends AppCompatActivity {
protected Button mTop;
protected Button mSide;
protected FrameLayout mRootLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_cutout);
initView();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && Build.VERSION.SDK_INT >= 19) {
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(option);
}
}
private void initView() {
mTop = (Button) findViewById(R.id.top);
mSide = (Button) findViewById(R.id.side);
mRootLayout = (FrameLayout) findViewById(R.id.rootLayout);
if (Build.VERSION.SDK_INT>=28){
mRootLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout!=null){
//获取四周的安全距离
int left = displayCutout.getSafeInsetLeft();
int top = displayCutout.getSafeInsetTop();
int right = displayCutout.getSafeInsetRight();
int bottom = displayCutout.getSafeInsetBottom();
//设置间距
FrameLayout.LayoutParams topLayoutParams = (FrameLayout.LayoutParams) mTop.getLayoutParams();
topLayoutParams.setMargins(left,top,right,bottom);
FrameLayout.LayoutParams sideLayoutParams = (FrameLayout.LayoutParams) mSide.getLayoutParams();
sideLayoutParams.setMargins(left,top,right,bottom);
}
return insets.consumeSystemWindowInsets();
}
});
}
}
}
清单文件
<activity android:name=".cutout.CutoutActivity"
android:screenOrientation="landscape"
android:theme="@style/CutoutTheme">
</activity>
v28/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CutoutTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">
shortEdges
</item>
</style>
</resources>
感谢郭婶:https://blog.csdn.net/guolin_blog/article/details/103112795