窗口管理

ChromeOS 支持在多个窗口中使用 Android 应用。系统会将应用呈现到窗口容器中,窗口容器的大小由设备类型决定,如图 1 所示。

不同设备上的应用窗口。
图 1:在此处添加图片说明。

图 1. 不同设备上的应用窗口。

设计能够适应不同屏幕尺寸的布局非常重要。如果您遵循 Android 指南来支持不同的屏幕尺寸,那么您的应用在 ChromeOS 上运行时也能正常运行。

本页介绍如何帮助应用窗口正确启动、顺畅地调整大小,并在其大小发生变化时显示全部内容。

初始启动大小

应用可以通过以下方式请求其初始启动大小:

  • 仅在桌面环境中使用启动大小。这有助于窗口管理器为您提供适当的边界和方向。如需在桌面模式下使用时指明偏好设置,请在 <activity> 内添加以下元标记:

    &lt;meta-data android:name=&#34;WindowManagerPreference:FreeformWindowSize&#34;
               android:value=&#34;[phone|tablet|maximize]&#34; /&gt;
    &lt;meta-data android:name=&#34;WindowManagerPreference:FreeformWindowOrientation&#34;
               android:value=&#34;[portrait|landscape]&#34; /&gt;
    
  • 使用静态启动边界。您可以使用 activity 的清单条目内的 <layout> 来指定“固定”起始大小,如以下示例所示:

    &lt;layout android:defaultHeight=&#34;500dp&#34;
                android:defaultWidth=&#34;600dp&#34;
                android:gravity=&#34;top|end&#34;
                android:minHeight=&#34;450dp&#34;
                android:minWidth=&#34;300dp&#34; /&gt;
    
  • 使用动态启动边界。创建新的 Activity 时,Activity 可以创建和使用 ActivityOptions.setLaunchBounds(Rect)。通过指定一个空矩形,您的应用可以最大化。

调整窗口大小

在 ChromeOS 中,用户可按常规方式(即拖动右下角)调整应用窗口的大小,如图 2 所示。

图 2.:在此处添加图片说明。

图 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().screenWidthActivity.getResources().getConfiguration().screenHeight。您或许永远都不需要使用屏幕大小。

内容边界

窗口的内容边界可能会随着大小调整而发生变化。例如,如果窗口太大以致屏幕容纳不下,那么应用使用的窗口区域就可能会发生变化。请遵循以下准则:

  • 使用 Android 布局过程的应用会自动在可用空间中进行布局。
  • 原生应用需要读取可用区域并监控大小变化,以避免出现无法访问的界面元素。您可以调用以下方法来确定相应界面的初始可用大小:

    • 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 的视图系统,则当窗口大小发生变化时,窗口布局会自动发生变化。
  • 如果您不使用视图系统并接管界面,则您的应用必须自行处理大小变化。
  • 对于原生应用,请使用 mLastContent 成员或使用内容视图来确定初始大小。
  • 当应用正在运行时,应监听 onContentRectChangedNativeonGlobalLayout 事件来响应大小变化。
  • 当应用的大小发生变化时,重新缩放或重新加载布局和图片并更新输入区域。

全屏模式

全屏模式与原生 Android 上的工作方式一样。如果窗口未覆盖全屏,则对全屏显示(隐藏所有系统界面元素)的请求会被忽略。当应用最大化时,会执行常规全屏方法、布局和功能。这样会隐藏系统界面元素(窗口控制栏和任务栏)。

屏幕方向

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_KEYGUARDTYPE_APPLICATION_MEDIA
  • 通过缓存先前分配的对象来确保实现快速的 activity 重启。
  • 如果您不希望用户调整应用的大小,请在清单文件中指定 android:resizeableActivity=false
  • 对您的应用进行测试,以确保其能够妥善处理窗口大小的变化。