最后,下图对应的是混合模式的刘海。
了解了各种不同的刘海模式,以及其对应的模拟方式,这样我们就将准备工作都完成了,接下来终于可以进入到具体的编码适配环节了。
思考一下,其实对于刘海屏的适配并不应该是一件复杂的事情,因为我们的目标很简单,就是不要让刘海部分遮挡到应用程序,或者影响到应用程序的正常使用即可。
为此,Android 9.0系统中提供了3种layoutInDisplayCutoutMode属性来允许应用自主决定该如何对刘海屏设备进行适配。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:这是一种默认的属性,在不进行明确指定的情况下,系统会自动使用这种属性。这种属性允许应用程序的内容在竖屏模式下自动延伸到刘海区域,而在横屏模式下则不会延伸到刘海区域。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:这种属性表示,不管手机处于横屏还是竖屏模式,都会允许应用程序的内容延伸到刘海区域。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:这种属性表示,永远不允许应用程序的内容延伸到刘海区域。
代码实现了解了以上内容之后,接下来我们就可以动手进行实现了。首先创建一个Demo项目,并让Android Studio帮我们自动生成一个空的Activity。在不编写任何额外代码的情况下直接运行该项目,效果如下图所示。
可以看到,在竖屏模式下应用程序的状态栏部分刚好占据了手机的刘海区域,并且系统还会根据刘海的高度来自动调整状态栏的高度,这样应用程序中的内容自然是不会被刘海部分遮挡掉的。
现在如果我们旋转一下手机,横屏模式下的效果如下图所示。
这个时候,手机的刘海区域会整个变成一条大黑边,应用程序的内容是不允许延伸到这部分区域里的,这样也不会产生内容被遮挡的情况。
也就是说,即使我们不做任何的适配工作,绝大多数的程序在默认情况下也是可以自动适配刘海屏手机的,并不会产生应用程序无法使用等问题的发生。但是,假如你开发的是一款视频类应用或者游戏的话,充分利用屏幕的空间明显可以带来更好的用户体验,界面上留着一条大黑边对用户总归是不够友好的。这个时候我们就可以通过指定layoutInDisplayCutoutMode属性的值,来让应用程序具备更好的屏幕适配性。
这里我就使用LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES属性,并且配合着沉浸式模式的代码,来编写一个全屏的UI界面,以此模拟视频和游戏类App的效果。
首先为了防止界面出现一片空白的情况,我对activity_main.xml布局的内容进行了修改,如下所示:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
tools:context=".MainActivity">
<Button
android:layout_width="150dp"
android:layout_height="80dp"
android:text="button one"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="150dp"
android:layout_height="80dp"
android:text="button two"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里给最外层的FrameLayout指定了一个背景图,随便使用什么图片都可以,我们只是为了便于进行演示。然后添加两个按钮,用于模拟操作按钮被刘海遮挡
接下来修改MainActivity中的代码,为其指定LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES属性。另外,为了让界面效果更加贴近于视频应用或游戏,这里我将MainActivity调整成了沉浸式模式,代码如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
setContentView(R.layout.activity_main)
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
supportActionBar?.hide()
if (Build.VERSION.SDK_INT >= 28) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
}
}
}
其实这段代码需要我们关心的就是if (Build.VERSION.SDK_INT >= 28)的这个逻辑判断中的内容,在这里我们将当前Activity的layoutInDisplayCutoutMode属性指定成LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,这样就可以让应用程序的内容延伸到刘海区域了。
现在重新运行一下程序,效果如下图所示。