窗口尺寸类别是一组有观点的视口断点,可帮助您设计、开发和测试响应式/自适应布局。这些断点在布局简单性和针对独特用例优化应用的灵活性之间取得平衡。
窗口尺寸类别将应用可用的显示区域分类为紧凑型、中型或扩展型。可用宽度和高度分别进行分类,因此在任何时间点,您的应用都有两个窗口尺寸类别——一个用于宽度,一个用于高度。由于垂直滚动的普遍存在,可用宽度通常比可用高度更重要,因此宽度窗口尺寸类别可能与您的应用 UI 更相关。
如图所示,这些断点允许您继续根据设备和配置来考虑布局。每个尺寸类别断点代表典型设备场景中的主要情况,这在您考虑基于断点的布局设计时,可以作为有用的参考框架。
尺寸类别 | 断点 | 设备表示 |
---|---|---|
紧凑宽度 | 宽度 < 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 应用可以显示在任意可调整大小的自由形式窗口中。折叠屏设备可以有两个不同尺寸的屏幕,通过折叠或展开设备分别访问。
窗口尺寸类别可以在应用的生命周期内发生变化。在应用运行期间,设备方向变化、多任务处理以及折叠/展开操作都会改变可用的屏幕空间。因此,窗口尺寸类别是动态的,并且应用的 UI 应该相应地进行调整。
窗口尺寸类别映射到Material Design 布局指南中的紧凑型、中型和扩展型断点。使用窗口尺寸类别做出高级应用布局决策,例如决定是否使用特定的规范布局来利用额外的屏幕空间。
您可以使用Jetpack WindowManager库提供的WindowSizeClass
的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. } }
测试窗口尺寸类别
在进行布局更改时,请测试所有窗口大小下的布局行为,尤其是在紧凑型、中型和扩展型断点宽度处。
如果您有现有的紧凑型屏幕布局,请首先针对扩展宽度尺寸类别优化您的布局,因为此尺寸类别为其他内容和 UI 更改提供了更多空间。然后决定哪种布局适合中等宽度尺寸类别;考虑添加专门的布局。
后续步骤
要详细了解如何使用窗口尺寸类别创建响应式/自适应布局,请参阅以下内容
对于基于 Compose 的布局:支持不同的屏幕尺寸
对于基于视图的布局:使用视图进行响应式/自适应设计
要详细了解哪些因素使应用在所有设备和屏幕尺寸上都表现出色,请参阅