限制应用在手机和可折叠设备外屏上的屏幕方向,但不限制其在较大显示屏上的屏幕方向

您的应用在手机上的纵向模式下效果好,因此您将应用限制为仅限纵向显示。但是您发现,对于大屏设备或展开的可折叠设备而言,该应用在横向模式下或许可以实现更多优势。

如何同时兼顾两种场景?也就是说,如何让应用在可折叠设备的外屏上仅可纵向显示,但在内屏上也可横向显示?

在您改进应用以提供对所有设备配置的全面支持之前,本指南将作为临时措施。

结果

在小屏设备上,无论设备如何旋转,应用都会始终保持纵向模式。在大屏设备上,应用支持横向模式和纵向模式。

版本兼容性

此实现与所有 API 级别兼容。

依赖项

Kotlin

implementation("androidx.window:window:1.5.1")
implementation("androidx.window:window-core:1.5.1")

Groovy

implementation "androidx.window:window:1.5.1"
implementation "androidx.window:window-core:1.5.1"

管理应用屏幕方向

如需在大屏设备上启用横向模式,请将应用清单设置为默认处理屏幕方向变化。在运行时,确定应用窗口大小。如果应用窗口较小,请替换清单屏幕方向设置,以限制应用的屏幕方向。

1. 在应用清单中指定屏幕方向设置

您可以避免声明应用清单的 screenOrientation 元素(在这种情况下,屏幕方向默认设置为 unspecified),也可以将屏幕方向设置为 fullUser。如果用户没有锁定基于传感器的旋转,您的应用将支持所有设备屏幕方向。

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

unspecifiedfullUser 之间的区别很细微,但很重要。如果您未声明 screenOrientation 值,系统会选择屏幕方向,并且系统用于定义屏幕方向的政策可能因设备而异。

另一方面,指定 fullUser 与用户为设备定义的行为更相符:如果用户锁定了基于传感器的旋转,应用会遵循用户的偏好设置;否则,系统会允许设备处于四种可能的屏幕方向(纵向、横向、反向纵向或反向横向)中的任何一种。

此外,您可以使用 nosensor 来确定屏幕方向,而无需考虑传感器数据,但以下代码将以相同的方式运行。 请参阅 screenOrientation

2. 确定屏幕尺寸

在清单中设置为支持用户允许的所有屏幕方向后,您就能以编程方式根据屏幕尺寸指定应用的屏幕方向。

Jetpack WindowManager 库添加到模块的 build.gradlebuild.gradle.kts 文件中:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

使用 Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics() 方法可获取 设备屏幕尺寸(作为 WindowMetrics 对象)。窗口指标可与窗口大小类进行比较,以确定何时限制屏幕方向。

窗口大小类提供了小屏和大屏之间的断点 。

使用 WindowSizeClass#minWidthDpWindowSizeClass#minHeightDp 断点确定屏幕尺寸:

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass =
        BREAKPOINTS_V1.computeWindowSizeClass(width / density, height / density)
    return windowSizeClass.minWidthDp == 0
}
    注意
  • 这些示例以 activity 的方法实现;因此,在 computeMaximumWindowMetrics() 的实参中,activity 会被取消引用为 this
  • 之所以使用 computeMaximumWindowMetrics() 方法而不是 computeCurrentWindowMetrics(),是因为应用可以在多窗口模式下启动,而多窗口模式会忽略屏幕方向设置。除非应用窗口占满整个设备屏幕,否则确定应用窗口大小并替换屏幕方向设置没有意义。

如需了解有关声明依赖项以使 computeMaximumWindowMetrics()方法在您的应用中可用的说明,请参阅 WindowManager

3. 替换应用清单设置

确定设备具有紧凑型屏幕尺寸后,您可以调用 Activity#setRequestedOrientation() 以替换清单的 screenOrientation 设置:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

通过将逻辑添加到 onCreate()View.onConfigurationChanged() 方法,您可以在 activity 调整大小或在显示屏之间移动时(例如在设备旋转后或可折叠设备折叠或展开时)获取最大窗口指标并替换屏幕方向设置。 如需详细了解何时配置会更改以及何时会导致 activity 重新创建,请参阅处理配置更改

如果您使用的是 Jetpack Compose,则可以在应用的根 Composable 中使用相同的 compactScreen() 函数来实现相同的结果。

要点

包含本指南的集合

本指南属于以下精选的快速指南集合,这些集合涵盖了更广泛的 Android 开发目标:

让您的应用能够在平板电脑、可折叠设备和 ChromeOS 设备上支持经过优化的用户体验。

有疑问或反馈意见

前往我们的常见问题解答页面,了解有关快速指南的信息;或者联系我们并告知您的想法。