窗口大小类别是一组主观的视口断点,有助于您设计、开发和测试响应式/自适应布局。这些断点可以平衡布局简单性与针对独特情形优化应用的灵活性。
窗口大小类别将可供应用使用的显示区域分类为较小、中等或展开。可用宽度和高度是单独分类的,因此在任何时间点,应用都有两个窗口大小类别:宽度窗口大小类别和高度窗口大小类别。由于垂直滚动的普遍存在,可用宽度通常比可用高度更重要,因此宽度窗口大小类别可能与应用的界面更相关。
如图所示,这些断点可让您继续从设备和配置的角度考虑布局。每个大小类别划分点代表了典型设备场景的大多数情况,当您考虑基于划分点的布局设计时,这可能是一个有用的参考框架。
大小类别 | 划分点 | 设备表示 |
---|---|---|
较小的宽度 | 宽度 < 600dp | 99.96% 的手机处于竖屏模式 |
中等宽度 | 600dp ≤ 宽度 < 840dp | 93.73% 的平板电脑处于竖屏模式,
最大展开状态的内屏(竖屏模式) |
较大宽度 | 宽度 ≥ 840dp | 97.22% 的平板电脑处于横屏模式,
展开状态下的最大内屏(横向显示) |
较小的高度 | 高度 < 480dp | 99.78% 的手机处于横屏模式 |
中等高度 | 480dp ≤ 高度 < 900dp | 96.56% 的平板电脑处于横屏模式,
97.59% 的手机处于竖屏模式 |
展开高度 | 高度 ≥ 900dp | 94.25% 的平板电脑处于竖屏模式 |
虽然将大小类别可视化为实体设备可能很有用,但窗口大小类别不是由设备屏幕尺寸明确决定的。窗口大小类别不适用于 isTablet 类型逻辑,相反,窗口大小类别由应用可用的窗口大小决定,而无论在哪种类型的设备上运行应用,这有两个重要影响:
实体设备不能保证特定的窗口大小类别。由于各种原因,应用可用的屏幕空间可能会与设备的屏幕尺寸不同。在移动设备上,分屏模式可以在两个应用之间对屏幕进行分区。在 ChromeOS 上,Android 应用可呈现在可任意调整大小的自由式窗口中。可折叠设备可以有两个大小不同的屏幕,分别可通过折叠或展开设备访问。
窗口大小类别在应用的整个生命周期内可能会发生变化。在应用运行时,设备屏幕方向更改、多任务处理和折叠/展开可能会改变可用的屏幕空间量。因此,窗口大小类别是动态的,应用的界面应相应地进行调整。
窗口大小类别对应于 Material Design 布局指南中的紧凑断点、中等断点和较大断点。 使用窗口大小类别做出粗略的应用布局决策,例如决定是否使用特定的规范布局以利用额外的屏幕空间。
您可以使用 Jetpack WindowManager 库提供的 WindowSizeClass#compute()
函数来计算当前的 WindowSizeClass
。以下示例展示了如何计算窗口大小类别,并在窗口大小类别发生变化时接收更新:
Kotlin
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // 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) computeWindowSizeClasses() } }) computeWindowSizeClasses() } private fun computeWindowSizeClasses() { val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass // COMPACT, MEDIUM, or EXPANDED val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass // Use widthWindowSizeClass and heightWindowSizeClass. } }
Java
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... // 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); computeWindowSizeClasses(); } }); computeWindowSizeClasses(); } private void computeWindowSizeClasses() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this); int width = metrics.getBounds().width int height = metrics.getBounds().height() float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass() // COMPACT, MEDIUM, or EXPANDED WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass() // Use widthWindowSizeClass and heightWindowSizeClass. } }
测试窗口大小类别
在更改布局时,应针对所有窗口大小测试布局行为,尤其是在较小、中等和较大断点宽度下。
如果您的现有布局适用于较小的屏幕,请先针对较大宽度大小类别优化布局,因为此大小类别可为额外的内容和界面更改提供最大的空间。然后,确定哪种布局对中等宽度大小类别有意义;考虑添加专用布局。
后续步骤
如需详细了解如何使用窗口大小类创建响应式/自适应布局,请参阅以下内容:
对于基于 Compose 的布局:支持不同的屏幕尺寸
对于基于 View 的布局:使用 View 实现响应式/自适应设计
如需详细了解如何让应用在所有设备上以及所有屏幕尺寸下都表现出色,请参阅: