您的应用在手机上的纵向模式下效果好,因此您将应用限制为仅限纵向显示。但是您发现,对于大屏设备而言,该应用在横向模式下或许可以实现更多优势。
如何同时兼顾两种场景?也就是说,如何让应用在小屏设备上仅可纵向显示,但在大屏设备上也可横向显示?
在您能够改进应用,使其针对所有设备配置提供全面支持之前,本指南只是一项临时措施。
管理应用屏幕方向
如需在大屏设备上启用横向屏幕方向,请将应用清单设置为默认处理屏幕方向变化。在运行时,确定应用窗口大小。如果应用窗口很小,则通过替换清单中的屏幕方向设置来限制应用的屏幕方向。
1. 在应用清单中指定屏幕方向设置
您可以避免声明应用清单的 screenOrientation
元素(在这种情况下,屏幕方向默认为 unspecified
),或将屏幕方向设为 fullUser
。如果用户没有锁定基于传感器的旋转,您的应用将支持所有设备屏幕方向。
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
unspecified
和 fullUser
之间的区别虽然微不足道,但很重要。如果您未声明 screenOrientation
值,系统会选择屏幕方向,并且系统用于定义屏幕方向的政策可能会因设备而异。另一方面,指定 fullUser
与用户为设备定义的行为更接近:如果用户锁定了基于传感器的旋转,应用会遵循用户的偏好设置;否则,系统会允许所有四种可能的屏幕方向(纵向、横向、反向纵向或反向横向)。请参阅 screenOrientation
。
2. 确定屏幕尺寸
在清单中设置为支持用户允许的所有屏幕方向后,您就能以编程方式根据屏幕尺寸指定应用的屏幕方向。
将 Jetpack WindowManager 库添加到模块的 build.gradle
或 build.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
对象)。窗口指标可与窗口大小类进行比较,以确定何时限制屏幕方向。
窗口大小类提供小屏幕和大屏幕之间的断点。
使用 WindowWidthSizeClass#COMPACT
和 WindowHeightSizeClass#COMPACT
断点来确定屏幕尺寸:
Kotlin
/** 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 = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- 注意:
- 这些示例作为 activity 的方法实现;因此,系统会在
computeMaximumWindowMetrics()
的参数中将 activity 作为this
解除引用。 - 示例中使用
computeMaximumWindowMetrics()
方法代替computeCurrentWindowMetrics()
,因为应用可以在多窗口模式下启动,该模式会忽略屏幕方向设置。除非应用窗口占满整个设备屏幕,否则确定应用窗口大小并替换屏幕方向设置没有意义。
如需了解如何声明依赖项以使 computeMaximumWindowMetrics()
方法在您的应用中可用,请参阅 WindowManager。
3. 替换应用清单设置
确定设备的屏幕尺寸较小后,可以调用 Activity#setRequestedOrientation()
来替换清单的 screenOrientation
设置:
Kotlin
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 } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(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. ViewGroup container = 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(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
通过将逻辑添加到 onCreate()
和 View.onConfigurationChanged()
方法,您可以获取最大窗口指标,并且每当 activity 调整大小或在不同显示屏之间移动(例如在设备旋转后或者可折叠设备折叠或展开时)时,均可替换屏幕方向设置。如需详细了解何时配置会更改以及何时会导致 activity 重新创建,请参阅处理配置更改。
要点
screenOrientation
:应用清单设置,让您可以指定应用如何响应设备屏幕方向的变化- Jetpack WindowManager:一组库,可让您确定应用窗口的尺寸和宽高比;向后兼容 API 级别 14
Activity#setRequestedOrientation()
:用于在运行时更改应用屏幕方向的方法
结果
现在,在小屏设备上,无论设备如何旋转,应用应该会始终保持纵向模式。在大屏设备上,该应用应支持横向模式和纵向模式。
包含本指南的集合
本指南属于以下精选快速入门指南集合,这些指南涵盖了更广泛的 Android 开发目标:
![](https://developer.android.com/static/images/quick-guides/collection-illustration.png?hl=zh-cn)