ChromeOS 在多个窗口中支持 Android 应用。系统将应用呈现到窗口容器中,其大小由设备的外形尺寸决定,如图 1 所示。
设计适用于不同屏幕尺寸的布局非常重要。如果您遵循 Android 指南 支持不同的屏幕尺寸,则您的应用在 ChromeOS 上运行时也能正常工作。
此页面显示如何确保应用的窗口能够正确启动、平滑调整大小并在其大小发生变化时显示所有内容。
初始启动大小
应用可以通过以下方式请求其初始启动大小
- 仅在桌面环境中使用启动大小。这有助于窗口管理器为您提供正确的边界和方向。若要在桌面模式下使用时指示首选项,请在
<activity>
内添加以下元标记
<meta-data android:name="WindowManagerPreference:FreeformWindowSize" android:value="[phone|tablet|maximize]" /> <meta-data android:name="WindowManagerPreference:FreeformWindowOrientation" android:value="[portrait|landscape]" />
- 使用静态启动边界。在活动清单条目内使用
<layout>
指定“固定”的起始大小,如下例所示
<layout android:defaultHeight="500dp" android:defaultWidth="600dp" android:gravity="top|end" android:minHeight="450dp" android:minWidth="300dp" />
- 使用动态启动边界。活动可以在创建新活动时创建和使用
ActivityOptions.setLaunchBounds(Rect)
。通过指定一个空矩形,您的应用可以最大化。
调整窗口大小
在 ChromeOS 中,用户可以通过通常的方式调整应用窗口的大小:拖动右下角,如图 2 所示。
当使用View
类时,处理窗口大小调整有两种选择。
- 通过调用
onConfigurationChanged(..)
动态响应配置更改。例如,您可以将android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout"
添加到 Activity 的清单中。有关处理配置更改的更多信息,请阅读处理配置更改。 - 让系统重新启动 Activity。在这种情况下,实现
onSaveInstanceState
并使用ViewModel 架构组件恢复先前保存的状态。
当使用Jetpack Compose时,调整大小的行为取决于 Activity 的配置方式。如果它动态处理更改,则当窗口大小发生变化时会触发重新组合。如果 Activity 由系统重新启动,则在重新启动后会发生初始组合。无论哪种方式,创建能够适应窗口大小变化的 Compose 布局都非常重要。不要假设固定大小。
窗口尺寸
让您的 Activity 在每次启动时读取其窗口尺寸,并根据当前配置排列其内容。
要确定当前配置,请在当前 Activity 上调用getResources().getConfiguration()
。不要使用后台 Activity 或系统资源的配置。后台 Activity 没有大小,系统配置可能包含多个具有冲突大小和方向的窗口,因此无法提取任何可用数据。
请注意,窗口大小和屏幕大小并不相同。要以 DP 为单位获取窗口大小,请使用Activity.getResources().getConfiguration().screenWidth
和Activity.getResources().getConfiguration().screenHeight
。您可能永远不需要使用屏幕大小。
内容边界
窗口的内容边界在调整大小后可能会发生变化。例如,如果窗口变得太大而无法适应屏幕,则应用程序使用的窗口内区域可能会发生变化。请遵循以下指南
- 使用 Android 布局过程的应用程序会在可用空间中自动布局。
原生应用程序需要读取可用区域并监控大小变化,以避免出现无法访问的 UI 元素。调用以下方法以确定此表面的初始可用大小
NativeActivity.mLastContent[X/Y/Width/Height]()
findViewById(android.R.id.content).get[Width/Height]()
可以使用观察者进行持续监控
NativeActivity.onContentRectChangedNative()
NativeActivity.onGlobalLayout()
- 向
view.addOnLayoutChangeListener(findViewById(android.R.id.content))
添加侦听器。
如果应用程序正在预缩放其艺术作品,则每次分辨率更改时都应这样做。
自由形式调整大小
ChromeOS 允许任何窗口自由调整大小:用户可以更改窗口的宽度、高度以及屏幕上的位置。许多 Android 应用程序在编写时没有考虑自由形式调整大小。请考虑以下问题
- 屏幕位置可能会发生变化。始终使用系统执行窗口到屏幕和屏幕到窗口的坐标转换。
- 如果您使用的是 Android 的 View 系统,则窗口布局会在其大小发生变化时自动更改。
- 如果您不使用 View 系统并接管表面,则您的应用程序必须自行处理大小更改。
- 对于原生应用程序,请使用
mLastContent
成员或使用内容视图来确定初始大小。 - 当应用程序运行时,侦听
onContentRectChangedNative
或onGlobalLayout
事件以对大小更改做出反应。 - 当应用程序的大小发生变化时,重新缩放或重新加载布局和艺术作品,并更新输入区域。
全屏模式
全屏模式与原生 Android 上的工作方式相同。如果窗口未覆盖整个屏幕,则忽略全屏请求(隐藏所有系统 UI 元素)。当应用程序最大化时,将执行正常的全屏方法、布局和功能。这将隐藏系统 UI 元素(窗口控制栏和底部的工具栏)。
屏幕方向
Android 应用程序最常见的方向是纵向,因为大多数手机都是这样握持的。虽然纵向模式对手机来说很好,但对于笔记本电脑和平板电脑来说却很糟糕,因为它们更喜欢横向模式。为了获得最佳效果,请考虑支持两种方向。
一些 Android 应用程序假设当设备以纵向模式握持时,旋转值为Surface.ROTATION_0
。这对于大多数 Android 设备来说可能是正确的。但是,当应用程序处于某些ARC模式下时,纵向方向的旋转值可能不是Surface.ROTATION_0
。
要获得读取加速度计或类似传感器时的准确旋转值,请使用Display.getRotation()
方法并相应地交换轴。
根 Activity 和方向
Chromebook 窗口由 Activity 窗口堆栈组成。堆栈中的每个窗口都具有相同的大小和方向。
在桌面环境中,突然的方向和大小变化令人困惑。Chromebook 窗口管理器以类似于 Android 并排模式的方式避免这种情况:堆栈底部的 Activity 控制其上所有 Activity 的属性。这可能导致意外情况,例如新启动的 Activity 纵向且不可调整大小,但变成横向且可调整大小。
设备模式在这里起作用:在平板电脑模式下,方向不会锁定,并且每个窗口都保留其自身的方向,这在 Android 上很常见。
方向指南
请遵循以下指南处理方向
- 如果您只支持一种方向,请将信息添加到清单中,以便窗口管理器在启动应用程序之前了解它。当您指定方向时,也请尽可能指定传感器方向。Chromebook 通常是可转换设备,而倒置的应用程序会带来糟糕的用户体验。
- 尽量保持单一选定的方向。避免在清单中请求一个方向,然后稍后以编程方式设置另一个方向。
- 小心根据窗口大小更改方向。用户可能会卡在一个小的纵向窗口中,无法返回到更大的横向窗口。
- Chrome 中有窗口控件可在所有可用布局之间切换。通过选择正确的方向选项,您可以确保用户在启动应用程序后具有正确的布局。如果应用程序可在纵向和横向模式下使用,则尽可能将其默认为横向模式。设置此选项后,它会在每个应用程序的基础上记住。
- 尽量避免不必要的方向更改。例如,如果 Activity 方向为纵向,但应用程序在运行时调用
setRequestedOrientation(LANDSCAPE)
,这会导致不必要的窗口调整大小,这会让用户感到厌烦,并可能重新启动应用程序(如果应用程序无法处理它)。最好只设置一次方向,例如在清单中,并且仅在必要时更改它。
其他注意事项
在 ChromeOS 中使用 Android 应用程序时,还需要考虑以下几点
- 不要在 Activity 的
onDestroy
方法中调用finish()
。这会导致应用程序在调整大小时关闭,并且不会重新启动。 - 不要使用不兼容的窗口类型,例如
TYPE_KEYGUARD
和TYPE_APPLICATION_MEDIA
。 - 通过缓存先前已分配的对象,使 Activity 重新启动速度更快。
- 如果您不希望用户调整应用程序的大小,请在清单文件中指定
android:resizeableActivity=false
。 - 测试您的应用程序,以确保它能够适当地处理窗口大小的变化。