支持不同的屏幕尺寸   您所在的位置:网站首页 大屏幕的尺寸 支持不同的屏幕尺寸  

支持不同的屏幕尺寸  

2023-09-26 14:36| 来源: 网络整理| 查看: 265

支持不同的屏幕尺寸会让您的应用尽量覆盖更多用户和各种各样的设备。

为了尽量支持更多屏幕尺寸,您的应用应该采用自适应布局。自适应布局提供经过优化的用户体验(无论屏幕尺寸如何),使您的应用可以适应手机、平板电脑、可折叠设备和 Chrome OS 设备、竖屏和横屏方向以及可调整大小的配置(例如多窗口模式)。

窗口大小类别

窗口大小类别是一组主观的视口划分点,有助于您设计、开发和测试自适应的应用布局。这些划分点是我们专门选择的,目的是平衡布局简单性与灵活性,以便针对独特情形优化您的应用。

窗口大小类别将应用中可用的显示区域分类为:较小、中等或展开。可用宽度和高度是单独分类的,因此在任何时间点,应用都有两个窗口大小类别:宽度窗口大小类别和高度窗口大小类别。但由于垂直滚动的普遍存在,可用宽度通常比可用高度更重要;因此,宽度窗口大小类别很可能与应用的界面更相关。

图 1. 基于宽度的窗口大小类别图示。 图 2. 基于高度的窗口大小类别图示。

如上所示,这些划分点让您可以继续从设备和配置的角度考虑布局。每个大小类别划分点代表了典型设备场景的大多数情况,当您考虑基于划分点的布局设计时,这可能是一个有用的参考框架。

大小类别 划分点 设备表示 较小的宽度 小于 600dp 99.96% 的手机处于竖屏模式 中等宽度 600dp+ 93.73% 的平板电脑处于竖屏模式,

展开的大型内部显示屏处于竖屏模式

展开宽度 840dp+ 97.22% 的平板电脑处于横屏模式,

展开的大型内部显示屏处于横屏模式

较小的高度 小于 480dp 99.78% 的手机处于横屏模式 中等高度 480dp+ 96.56% 的平板电脑处于横屏模式,

97.59% 的手机处于竖屏模式

展开高度 900dp+ 94.25% 的平板电脑处于竖屏模式 注意:大多数应用在构建自适应界面时可以只考虑宽度窗口大小类别。

虽然使用实体设备直观地查看大小类别很有用,但窗口大小类别不是由设备屏幕的尺寸明确决定的。窗口大小类别不适用于“isTablet-type”逻辑,而是由应用可用的窗口大小决定(无论运行应用的设备是什么类型);这有两个重大影响:

实体设备不能保证特定的窗口大小类别。应用可用的屏幕空间可能会与设备的屏幕尺寸不同,这有很多原因。在移动设备上,分屏模式可以在多个应用之间拆分屏幕。在 Chrome 操作系统中,Android 应用可以呈现在可任意调整大小的自由式窗口中。可折叠设备可以有两个大小不同的屏幕,分别可通过折叠或展开设备使用。

窗口大小类别在应用的整个生命周期内可能会发生变化。当应用处于运行状态时,设备更改屏幕方向、进行多任务处理和折叠/展开可能会改变可用的屏幕空间量。因此,窗口大小类别是动态的,应用的界面应相应地调整。

窗口大小类别与 Material Design 自适应布局网格中的布局划分点一一对应。使用窗口大小类别来做出高级应用布局决策,如决定是否使用特定的规范布局以利用额外的屏幕空间。

基于视图的应用应根据 Jetpack WindowManager 库提供的当前窗口指标计算窗口大小类别。下方的视图 (Kotlin)/视图 (Java) 代码举例说明了如何根据划分点计算窗口大小类别,并在其发生变化时随时接收更新。

基于 Compose 的应用应根据 calculateWindowSizeClass() 提供的当前窗口指标,使用 material3-window-size-class 库计算 WindowSizeClass。

视图 enum class WindowSizeClass { COMPACT, MEDIUM, EXPANDED } 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 it 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. We also 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 widthDp = metrics.bounds.width() / resources.displayMetrics.density val widthWindowSizeClass = when { widthDp < 600f -> WindowSizeClass.COMPACT widthDp < 840f -> WindowSizeClass.MEDIUM else -> WindowSizeClass.EXPANDED } val heightDp = metrics.bounds.height() / resources.displayMetrics.density val heightWindowSizeClass = when { heightDp < 480f -> WindowSizeClass.COMPACT heightDp < 900f -> WindowSizeClass.MEDIUM else -> WindowSizeClass.EXPANDED } // Use widthWindowSizeClass and heightWindowSizeClass. } } 视图 public enum WindowSizeClass { COMPACT, MEDIUM, EXPANDED } 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 it 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. We also 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); float widthDp = metrics.getBounds().width() / getResources().getDisplayMetrics().density; WindowSizeClass widthWindowSizeClass; if (widthDp < 600f) { widthWindowSizeClass = WindowSizeClass.COMPACT; } else if (widthDp < 840f) { widthWindowSizeClass = WindowSizeClass.MEDIUM; } else { widthWindowSizeClass = WindowSizeClass.EXPANDED; } float heightDp = metrics.getBounds().height() / getResources().getDisplayMetrics().density; WindowSizeClass heightWindowSizeClass; if (heightDp < 480f) { heightWindowSizeClass = WindowSizeClass.COMPACT; } else if (heightDp < 900f) { heightWindowSizeClass = WindowSizeClass.MEDIUM; } else { heightWindowSizeClass = WindowSizeClass.EXPANDED; } // Use widthWindowSizeClass and heightWindowSizeClass. } } Compose class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val windowSizeClass = calculateWindowSizeClass(this) MyApp(windowSizeClass) } } }

在应用中观察窗口大小类别之后,您就可以开始根据当前的窗口大小类别来改变布局了。

如需了解如何使用窗口大小类别让布局具备自适应能力,请参阅以下内容:

对于基于视图的布局,请参阅将界面迁移到自适应布局 对于基于 Compose 的布局,请参阅构建自适应布局 支持不同窗口大小类别的核对清单

当您做出布局更改时,请在各种窗口大小下测试布局行为,尤其是在较小、中等和展开式划分点宽度下。

如果您的某个现有布局适合较小的屏幕,请首先针对展开宽度大小类别优化布局,因为这样可以为额外的内容或界面变化提供最大的空间。然后,决定什么布局对中等宽度类别有意义,并考虑添加中等宽度屏幕大小的专用布局。

为了提供更好的用户体验,您可以添加专门适用于您的应用的功能,如支持可折叠设备的折叠状态或针对键盘、鼠标和触控笔输入支持进行优化。

如需详细了解如何让应用在所有设备上以及所有屏幕尺寸下都表现出色,请参阅大屏幕应用质量。

注意:本页的其余内容主要侧重于介绍如何使用基于视图的布局来支持不同的屏幕尺寸。有关 Compose 的指导,请参阅构建自适应布局和其他 Compose 文档。

自适应设计

如需支持各种设备外形规格,第一步就是制作一个能够灵活应对屏幕尺寸变化的布局。

ConstraintLayout

如需制作自适应布局,最好的方法是将 ConstraintLayout 用作界面中的基本布局。使用 ConstraintLayout,您可以根据布局中视图之间的空间关系指定每个视图的位置和大小。当屏幕尺寸改变时,所有视图都可以随之移动和调整大小。

如需使用 ConstraintLayout 构建布局,最简单的方法是使用 Android Studio 中的布局编辑器。借助布局编辑器,您可以将新视图拖动到布局中,应用与父视图和同级视图相关的约束条件,以及设置视图的属性,完全不必手动编辑任何 XML。

图 3. Android Studio 中的布局编辑器,显示了一个 ConstraintLayout。

如需了解详情,请参阅使用 ConstraintLayout 构建自适应界面。

自适应宽度和高度

为了确保布局能够灵活地适应不同的屏幕尺寸,可以对大多数视图组件的宽度和高度使用 wrap_content、match_parent 或 0dp (match constraint),而不是硬编码的值:

wrap_content - 让视图可将自身大小设为适应视图中所含内容必需的大小。 match_parent - 让视图可在父视图中尽可能地展开。 0dp (match constraint) - 在 ConstraintLayout 中,类似于 match_parent。让视图可填充约束条件范围内的所有可用空间。

例如:

图 4 显示了当屏幕宽度随着设备屏幕方向而发生变化时,TextView 的宽度和高度会如何调整。

图 4. 一个自适应 TextView。

该 TextView 将其宽度设为填充所有可用空间 (match_parent),并将其高度设为正好是所含文本的高度所需的空间 (wrap_content),从而使此视图适应不同的屏幕尺寸和不同的文本量。

如果您使用的是 LinearLayout,还可以按布局权重展开子视图,以便视图按比例填充可用的空间。但是,在嵌套的 LinearLayout 中使用权重将要求系统执行多次布局遍历以确定每个视图的大小,这会降低界面性能。

ConstraintLayout 几乎能够构建 LinearLayout 所能构建的所有布局,而不会影响性能,因此您应该尝试将嵌套的 LinearLayout 转换为 ConstraintLayout。然后,即可使用约束链定义加权布局。

注意:使用 ConstraintLayout 时,不得使用 match_parent,而应将尺寸设为 0dp 以启用一种称为“匹配约束”的特殊行为,该行为通常与 match_parent 的行为相同。如需了解详情,请参阅如何在 ConstraintLayout 中调整视图大小。

自适应设计

应用的布局应始终灵活适应不同的屏幕尺寸。不过,即使采用自适应布局,也无法在每部设备上都提供很好的用户体验。例如,您为手机设计的界面或许无法在平板电脑上提供优质的体验。自适应设计可提供针对不同显示屏尺寸进行了优化的备用布局。

对列表详情界面使用 SlidingPaneLayout

列表详情界面通常会在不同尺寸的屏幕上提供不同的用户体验。在大屏幕上,列表窗格和详情窗格通常并排显示。选择列表中的某项内容后,内容信息会显示在详情窗格中,而不会改变界面(两个窗格仍会并排显示)。但在小屏幕上,这两个窗格是分开显示的,每个窗格都会占据整个显示区域。选择列表窗格中的某项内容后,详情窗格(包含所选内容的信息)将替换列表窗格。返回导航会将详情窗格替换为列表窗格。

SlidingPaneLayout 可管理用于确定这两种用户体验中的哪一种适合当前窗口大小的逻辑:

SlidingPaneLayout 中包含的两个视图的 layout_width 和 layout_weight 属性会决定 SlidingPaneLayout 行为。在本例中,如果窗口足够大(至少 580dp 宽),能够同时显示两个视图,窗格便会并排显示。不过,如果窗口宽度小于 580dp,窗格会相互叠加并在叠加的窗格上滑动,以便分别占用整个应用窗口。

如果窗口宽度大于指定的最小总宽度 (580dp),可以使用 layout_weight 值按比例调整两个窗格的大小。在本例中,列表窗格将始终为 280dp 宽,因为它不具备权重。不过,由于视图的 layout_weight,详细信息窗格会始终填充超过 580dp 的所有横向空间。

注意:标准 layout_weight 行为有一个例外情况,那就是在可折叠设备上使用 SlidingPaneLayout v1.2.0 及更高版本;在此情况下,SlidingPaneLayout 将自动调整窗格的大小,以使其位于任何折叠边或合页的任意一侧。 备用布局资源

如需让界面设计能适应不断变化的屏幕尺寸,您可以使用由资源限定符标识的备用布局。

图 5. 同一应用显示在尺寸不同的屏幕上,针对每种屏幕尺寸使用不同的布局。

您可以通过在应用的源代码中创建额外的 res/layout/ 目录,提供特定于屏幕的布局。针对需要不同布局的每种屏幕配置创建一个目录。然后,将屏幕配置限定符附加到 layout 目录名称(例如,对于可用宽度为 600dp 的屏幕,附加限定符为 layout-w600dp)。

这些配置限定符表示应用界面可用的可见屏幕空间。为应用选择布局时,系统会考虑所有系统装饰(例如导航栏)和窗口配置更改(例如多窗口模式)。

如需在 Android Studio(使用 3.0 或更高版本)中创建备用布局,请按以下步骤操作:

打开默认布局,然后点击工具栏中的 Orientation for Preview 图标 。 在下拉列表中,点击以创建一个建议的变体(如 Create Landscape Variation),或点击 Create Other。 如果您选择 Create Other,系统将显示 Select Resource Directory。在左侧选择一个屏幕限定符,然后将其添加到 Chosen qualifiers 列表中。添加限定符之后,点击 OK。(有关屏幕尺寸限定符的信息,请参阅下面几部分。)

系统会在新的布局目录中创建默认布局文件的副本,以便您可以开始自定义该屏幕变体的布局。

“最小宽度”限定符

您可以使用“最小宽度”屏幕尺寸限定符,为具有以密度无关像素(dp 或 dip)衡量的最小宽度的屏幕提供备用布局。

在 Android 平台上,您可以用 dp 为度量单位来描述屏幕尺寸,从而针对具体的屏幕尺寸创建专用布局,而不必担心像素密度的变化。

例如,您可以创建一个名为 main_activity 且针对手机和平板电脑进行了优化的布局,方法是在不同的目录中创建该文件的不同版本:

res/layout/main_activity.xml # For phones (smaller than 600dp smallest width) res/layout-sw600dp/main_activity.xml # For 7” tablets (600dp wide or wider)

屏幕两侧的最小尺寸是由“最小宽度”限定符指定,与设备的当前屏幕方向无关;因此,这是一种指定可用于布局的整体屏幕尺寸的简单方法。

下面是其他最小宽度值与典型屏幕尺寸的对应关系:

320dp:典型手机屏幕(240x320 ldpi、320x480 mdpi、480x800 hdpi 等)。 480dp:约为 5 英寸的大手机屏幕 (480x800 mdpi) 600dp:7 英寸平板电脑 (600x1024 mdpi) 720dp:10 英寸平板电脑(720x1280 mdpi、800x1280 mdpi 等)

图 6 提供了一个更详细的视图,说明了不同屏幕 dp 宽度与不同屏幕尺寸和方向的一般对应关系。

图 6. 建议的宽度划分点,以支持不同的屏幕尺寸。

“最小宽度”限定符的数值是密度无关像素,因为重要的是系统考虑像素密度(而不是原始像素分辨率)之后可用的屏幕空间量。

您使用“最小宽度”等资源限定符指定的尺寸不是实际屏幕尺寸,相反,这类尺寸指定了应用窗口可用的宽度或高度(以 dp 为单位)。Android 系统可能会将部分屏幕用于系统界面(如屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能无法供您的布局使用。如果您的应用在多窗口模式下使用,则它只能获取应用自身所在窗口的尺寸。调整该窗口的大小时,它会使用新窗口尺寸触发配置更改,以便系统可以选择适当的布局文件。因此,在声明资源限定符尺寸时,您应仅说明您的应用所需的空间大小。在为您的布局提供空间时,系统会考虑系统界面使用的所有空间。

“可用宽度”限定符

您可能希望根据当前可用的宽度或高度更改布局,而不是根据屏幕的最小宽度更改布局。例如,您可能希望每当屏幕宽度至少为 600dp 时使用双窗格布局,但屏幕宽度可能会根据设备的屏幕方向是横屏还是竖屏而发生变化。在这种情况下,您应使用“可用宽度”限定符,如下所示:

res/layout/main_activity.xml # For phones (smaller than 600dp available width) res/layout-w600dp/main_activity.xml # For 7” tablets or any screen with 600dp available width # (possibly landscape phones)

如果您的应用注重可用高度,您可以使用“可用高度”限定符。例如,对于屏幕高度至少为 600dp 的屏幕,请使用限定符 layout-h600dp。

“屏幕方向”限定符

虽然您可能只需将“最小宽度”和“可用宽度”限定符结合使用,即可支持所有尺寸变化,但是您可能还希望当用户在竖屏与横屏之间切换时改变用户体验。

为此,您可以将 port 或 land 限定符添加到布局目录名称中。只需确保“屏幕方向”限定符在尺寸限定符后面即可。例如:

res/layout/main_activity.xml # For phones res/layout-land/main_activity.xml # For phones in landscape res/layout-sw600dp/main_activity.xml # For 7” tablets res/layout-sw600dp-land/main_activity.xml # For 7” tablets in landscape

如需详细了解所有屏幕配置限定符,请参阅应用资源概览。

使用 fragment 将界面组件模块化

在针对多种屏幕尺寸设计应用时,如需确保不会在 activity 之间不必要地重复界面行为,可以使用 fragment 将界面逻辑提取到单独的组件中。然后,您可以组合 fragment 以制作适用于大屏设备的多窗格布局,或者将 fragment 放置在适用于小屏设备的单独 activity 中。

例如,您可以使用一个包含列表的 fragment 和另一个包含列表项详情的 fragment,以实现列表详情模式(请参阅上文的 SlidingPaneLayout)。在大屏幕上,fragment 可以并排显示;在小屏幕上,则会分别填满屏幕。

如需了解详情,请参阅 fragment 概览。

activity 嵌入

如果您的应用由多个 activity 组成,activity 嵌入可让您轻松制作自适应界面。

借助 activity 嵌入,系统可在一个应用的任务窗口中同时显示多个 activity 或同一 activity 的多个实例。在大屏幕上,activity 可以并排显示;在小屏幕上,则会相互堆叠显示。

您可通过创建 XML 配置文件来确定应用如何显示其 activity,系统会利用该 XML 配置文件来确定适合屏幕大小的呈现方式。或者,您也可以进行 Jetpack WindowManager API 调用。

activity 嵌入支持设备屏幕方向变化和可折叠设备,该功能会随着设备旋转或折叠/展开而堆叠和取消堆叠 activity。

如需了解详情,请参阅 activity 嵌入。

屏幕尺寸和宽高比

您可以针对各种屏幕尺寸和宽高比测试您的应用,以确保界面正确缩放。

Android 10(API 级别 29)及更高版本支持各种宽高比。可折叠设备的外形规格丰富,从窄长的屏幕(如折叠时的长宽比为 21:9)到方形的屏幕(展开时的宽高比为 1:1),多种多样。

为确保与尽可能多的设备兼容,请尽可能多地针对以下屏幕宽高比来测试您的应用。

图 7. 屏幕宽高比。

如果无法支持上方某些高宽比,您可以使用 maxAspectRatio 和 minAspectRatio 指明应用可以处理的最高宽高比和最低宽高比。如果屏幕宽高比超出这些限制,系统可能会将您的应用置于兼容模式。

如果您身边没有具有各种不同屏幕尺寸的实体设备可供测试,则可以使用 Android 模拟器模拟任何屏幕尺寸。

如果您还是希望在真实设备上进行测试,但没有设备,则可以使用 Firebase Test Lab 访问 Google 数据中心内的设备。

支持特定的屏幕尺寸

如果您不想让应用以特定的屏幕尺寸运行,可以对应用的尺寸调整幅度设置限制,甚至可以根据屏幕配置来限制可安装您的应用的设备。如需了解详情,请参见声明受限屏幕支持。

其他资源 Material Design 3: Adaptive design(Material Design 3:自适应设计) Canonical layouts for adaptive design(适用于自适应设计的规范布局)


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有