多窗口模式允许多个应用同时共享同一屏幕。应用可以并排显示或一个叠加在另一个上面(分屏模式),一个应用以一个小窗口覆盖其他应用(画中画模式),或者各个应用位于单独的可移动、可调整大小的窗口中(桌面窗口模式)。
有关如何在手机上访问分屏模式的用户说明,请访问 在 Pixel 手机上同时查看两个应用。
特定版本的多窗口功能
多窗口用户体验取决于 Android 版本和设备类型
Android 7.0(API 级别 24)在小屏幕设备上引入了分屏模式,在特定设备上引入了画中画模式。
分屏模式使用两个应用填充屏幕,将它们并排显示或一个叠加在另一个上面。用户可以拖动分隔两个应用的分隔线,使一个应用更大,另一个应用更小。
画中画模式使用户能够在与其他应用交互时继续视频播放(请参阅 画中画支持)。
桌面窗口模式(其中用户可以自由调整每个活动的尺寸)可以由大屏幕设备的制造商启用。
您可以通过指定活动的最小允许尺寸来配置您的应用如何处理多窗口模式。您还可以通过设置
resizeabableActivity="false"
来禁用应用的多窗口模式,以确保系统始终以全屏显示您的应用。
Android 8.0(API 级别 26)将画中画模式扩展到小屏幕设备。
Android 12(API 级别 31)使多窗口模式成为标准行为。
分屏模式
用户通过执行以下操作激活分屏模式
- 打开 最近使用的应用屏幕
- 向上滑动一个应用以显示
- 按应用标题栏中的应用图标
- 选择分屏菜单选项
- 从最近使用的应用屏幕中选择另一个应用,或关闭最近使用的应用屏幕并运行另一个应用
用户通过将窗口分隔线拖动到屏幕边缘(向上或向下、向左或向右)来退出分屏模式。
启动相邻应用
如果您的应用需要通过意图访问内容,则可以使用 FLAG_ACTIVITY_LAUNCH_ADJACENT
在相邻的分屏窗口中打开内容。
FLAG_ACTIVITY_LAUNCH_ADJACENT
在 Android 7.0(API 级别 24)中引入,用于允许在分屏模式下运行的应用在相邻窗口中启动活动。
Android 12L(API 级别 32)及更高版本已扩展了该标志的定义,以允许以全屏模式运行的应用激活分屏模式,然后在相邻窗口中启动活动。
要启动相邻活动,请将 FLAG_ACTIVITY_LAUNCH_ADJACENT
与 FLAG_ACTIVITY_NEW_TASK
结合使用,例如
fun openUrlInAdjacentWindow(url:
String) { Intent(Intent.ACTION_VIEW).apply { data = Uri.parse(url)
addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or Intent.FLAG_ACTIVITY_NEW_TASK)
}.also { intent -> startActivity(intent) } }
多窗口模式下的活动生命周期
多窗口模式不会更改 活动生命周期。但是,多个窗口中应用的恢复状态在不同的 Android 版本上有所不同。
多恢复
Android 10(API 级别 29)及更高版本支持多恢复——当设备处于多窗口模式时,所有活动都保持在 RESUMED
状态。如果透明活动位于活动顶部或活动不可聚焦,则活动可能会被暂停,例如,活动处于 画中画模式。也可能在给定时间没有活动获得焦点,例如,如果通知抽屉已打开。 onStop()
方法的工作方式与往常一样:只要活动从屏幕上移开,就会调用该方法。
某些运行 Android 9(API 级别 28)的设备也提供多恢复功能。要在 Android 9 设备上启用多恢复,请添加以下清单元数据
<meta-data android:name="android.allow_multiple_resumed_activities" android:value="true" />
要验证给定设备是否支持此清单元数据,请参阅设备规格。
Android 9
在 Android 9(API 级别 28)及更低版本上的多窗口模式下,在给定时间仅用户最近交互过的活动处于活动状态。此活动被视为最顶层,并且是唯一处于 RESUMED
状态的活动。所有其他可见活动都处于 STARTED
状态,但未处于 RESUMED
状态。但是,系统会为这些可见但未恢复的活动提供比不可见活动更高的优先级。如果用户与其中一个可见活动进行交互,则该活动将恢复,而先前最顶层的活动将进入 STARTED
状态。
当单个活动应用进程中存在多个活动时,z 顺序最高的活动将恢复,其他活动将暂停。
配置更改
当用户将应用置于多窗口模式时,系统会通知活动配置更改,如 处理配置更改 中所述。当用户调整应用大小或将应用恢复到全屏模式时,也会发生这种情况。
从本质上讲,此更改与系统通知应用设备已从纵向切换到横向时的活动生命周期含义相同,只是应用尺寸发生了更改,而不是简单地交换。您的活动可以自行处理配置更改,或者您的应用可以允许系统销毁活动并使用新尺寸重新创建它。
如果用户正在调整窗口大小并使其在任一维度上变大,则系统会调整活动大小以匹配用户操作并根据需要发出配置更改。如果应用在绘制新公开区域方面滞后,则系统会使用 windowBackground
属性或默认 windowBackgroundFallback
样式属性中指定的颜色暂时填充这些区域。
独占资源访问
为了帮助支持多恢复功能,请使用 onTopResumedActivityChanged()
生命周期回调。
当活动获得或失去最顶层恢复活动位置时,将调用此回调,这在活动使用共享单例资源(如麦克风或摄像头)时非常重要
override fun
onTopResumedActivityChanged(topResumed: Boolean) { if (topResumed) { // Top
resumed activity. // Can be a signal to re-acquire exclusive resources. } else {
// No longer the top resumed activity. } }
请注意,应用可能会因其他原因而丢失资源,例如移除共享的硬件部件。
无论如何,应用都应优雅地处理影响可用资源的事件和状态更改。
对于使用摄像头的应用,CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()
提供了一个提示,表明现在可能是尝试获取摄像头访问权限的好时机。此方法在 Android 10(API 级别 29)及更高版本中可用。
请记住,resizeableActivity=false
并非独占摄像头访问的保证,因为其他使用摄像头的应用可以在其他显示屏上打开。
您的应用在失去焦点时不一定需要释放摄像头。例如,您可能希望在用户与新获得焦点的最顶层恢复的应用交互时继续进行摄像头预览。您的应用在不是最顶层恢复的应用时继续运行摄像头是可以的,但它必须正确处理断开连接的情况。当最顶层恢复的应用想要使用摄像头时,它可以打开摄像头,您的应用将失去访问权限。当应用重新获得焦点时,您的应用可以重新打开摄像头。
在应用收到CameraDevice.StateCallback#onDisconnected()
回调后,对摄像头设备的后续调用将抛出CameraAccessException
。
多显示器
Android 10(API 级别 29)支持在辅助显示器上运行活动。如果活动在具有多个显示器的设备上运行,用户可以将活动从一个显示器移动到另一个显示器。多恢复也适用于多屏幕场景;多个活动可以同时接收用户输入。
应用可以在启动时或创建另一个活动时指定它应该在哪个显示器上运行。此行为取决于清单文件中定义的活动启动模式以及启动活动的实体设置的意图标志和选项。有关更多详细信息,请参阅ActivityOptions
类。
当活动移动到辅助显示器时,它可能会经历上下文更新、窗口大小调整以及配置和资源更改。如果活动处理配置更改,则会在onConfigurationChanged()
中通知活动;否则,活动将重新启动。
如果处理配置更改,活动应该在onCreate()
和onConfigurationChanged()
中检查当前显示器。确保在显示器更改时更新资源和布局。
如果活动的所选启动模式允许多个实例,则在辅助屏幕上启动可能会创建一个新的活动实例。这两个活动同时恢复。
您可能还想阅读有关 Android 8.0 中引入的多显示器 API 的信息。
活动与应用上下文
在多显示器中使用正确的上下文至关重要。在访问资源时,活动上下文(正在显示的)与应用上下文(未显示的)不同。
活动上下文包含有关显示器的信息,并且始终针对活动显示的显示器区域进行调整。这使您能够获取有关应用的显示器密度或窗口指标的正确信息。您应该始终使用活动上下文(或其他基于 UI 的上下文)来获取有关当前窗口或显示器的信息。这也会影响使用上下文信息的一些系统 API(例如,请参阅Toast 概述)。
活动窗口配置和父显示器定义资源和上下文。获取当前显示器的方法如下
val activityDisplay = activity.getDisplay()
获取当前活动窗口指标
val windowMetrics = activity.getWindowManager().getCurrentWindowMetrics()
获取当前系统配置的最大窗口指标
val maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics()
最大窗口指标用于进行计算、布局选择或确定提前获取资源的大小。在onCreate()
中提供此功能使您能够在第一次布局传递之前做出这些决策。这些指标不应用于布局特定的视图元素;而是使用来自Configuration
对象的信息。
显示器挖孔
折叠式设备在折叠和展开时可能具有不同的挖孔几何形状。要避免挖孔问题,请参阅支持显示器挖孔。
辅助显示器
您可以从DisplayManager
系统服务获取可用的显示器
val displayManager =
getSystemService(Context.DISPLAY_SERVICE) as DisplayManager val displays =
displayManager.getDisplays()
使用Display
类获取有关特定显示器的信息,例如显示器大小或指示显示器是否安全的标志。但是,不要假设显示器大小将与分配给应用程序的显示器区域相同。请记住,在多窗口模式下,您的应用程序占据显示器的一部分。
确定活动是否可以在显示器上启动
val activityManager =
getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val
activityAllowed = activityManager.isActivityStartAllowedOnDisplay(context,
displayId, intent)
然后在显示器上启动活动
val options = ActivityOptions.makeBasic()
options.setLaunchDisplayId(targetDisplay.displayId) startActivity(intent,
options.toBundle())
多显示器支持
Android 为软件键盘、壁纸和启动器提供多显示器支持。
软件键盘
如果显示器配置为支持系统装饰,则可以在辅助屏幕上显示键盘。如果文本字段在该显示器上请求输入,则输入法编辑器会自动出现。
壁纸
在 Android 10(API 级别 29)中,辅助屏幕可以具有壁纸。框架为每个显示器创建WallpaperService.Engine
的一个单独实例。确保每个引擎的表面独立绘制。开发人员可以使用显示器上下文在WallpaperService.Engine#getDisplayContext()
中加载资源。此外,请确保您的WallpaperInfo.xml
文件设置android:supportsMultipleDisplays="true"
。
启动器
一个新的意图过滤器类别SECONDARY_HOME
为辅助屏幕提供了一个专用的活动。该活动的实例用于所有支持系统装饰的显示器,每个显示器一个。
<activity>
...
<intent-filter>
<category android:name="android.intent.category.SECONDARY_HOME" />
...
</intent-filter>
</activity>
活动必须具有不阻止多个实例并且可以适应不同屏幕尺寸的启动模式。启动模式不能是singleInstance
或singleTask
。
例如,Launcher3
的 AOSP 实现支持SECONDARY_HOME
活动。
窗口指标
Android 11(API 级别 30)引入了以下WindowManager
方法来提供在多窗口模式下运行的应用的边界
getCurrentWindowMetrics()
:返回一个WindowMetrics
对象,用于系统的当前窗口状态。getMaximumWindowMetrics()
:返回WindowMetrics
,用于系统的最大潜在窗口状态。
Jetpack WindowManager 库方法computeCurrentWindowMetrics()
和computeMaximumWindowMetrics()
分别提供了类似的功能,但具有向后兼容 API 级别 14 的功能。
要获取除当前显示器之外的显示器的指标,请执行以下操作(如代码片段所示)
- 创建显示器上下文
- 为显示器创建窗口上下文
- 获取窗口上下文的
WindowManager
- 获取应用可用的最大显示区域的
WindowMetrics
val windowMetrics =
context.createDisplayContext(display)
.createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
.getSystemService(WindowManager::class.java) .maximumWindowMetrics
已弃用的方法
Display
方法getSize()
和getMetrics()
在 API 级别 30 中已弃用,取而代之的是新的WindowManager
方法。
Android 12(API 级别 31)弃用了Display
方法getRealSize()
和getRealMetrics()
,并更新了它们的行为,使其更接近getMaximumWindowMetrics()
的行为。
多窗口模式配置
如果您的应用以 Android 7.0(API 级别 24)或更高版本为目标,则可以配置应用的活动如何以及是否支持多窗口模式。您可以在清单中设置属性以控制大小和布局。根活动的属性设置适用于其任务堆栈中的所有活动。例如,如果根活动具有android:resizeableActivity="true"
,则任务堆栈中的所有活动都可调整大小。在某些较大的设备(例如 Chromebook)上,即使您指定android:resizeableActivity="false"
,您的应用也可能在可调整大小的窗口中运行。如果这会破坏您的应用,您可以使用Google Play 上的过滤器来限制您的应用在这些设备上的可用性。
Android 12(API 级别 31)默认启用多窗口模式。在大屏幕设备上(中等 或 扩展 窗口尺寸类别),所有应用都以多窗口模式运行,无论应用配置如何。在小屏幕设备上,系统会检查 Activity 的 minWidth
、minHeight
和 resizeableActivity
设置以确定该 Activity 是否可以以多窗口模式运行。
resizeableActivity
在清单文件的 <activity>
或 <application>
元素中设置此属性,以启用或禁用 API 级别 30 及更低版本的应用的多窗口模式。
<application
android:name=".MyActivity"
android:resizeableActivity=["true" | "false"] />;
如果将此属性设置为 true
,则可以以分屏和桌面窗口模式启动该 Activity。如果将此属性设置为 false
,则该 Activity 不支持多窗口模式。如果该值为 false,并且用户尝试以多窗口模式启动该 Activity,则该 Activity 将占据整个屏幕。
如果您的应用以 API 级别 24 或更高版本为目标,但未为此属性指定值,则该属性的值默认为 true。
如果您的应用以 API 级别 31 或更高版本为目标,则此属性在小屏幕和大屏幕上的工作方式有所不同。
- **大屏幕**(中等 或 扩展 窗口尺寸类别):所有应用都支持多窗口模式。该属性指示 Activity 是否可以调整大小。如果
resizeableActivity="false"
,则在必要时将应用置于兼容性模式,以符合显示尺寸。 - **小屏幕**(紧凑 窗口尺寸类别):如果
resizeableActivity="true"
且 Activity 的最小宽度和最小高度在多窗口要求范围内,则该 Activity 支持多窗口模式。如果resizeableActivity="false"
,则该 Activity 不支持多窗口模式,无论 Activity 的最小宽度和高度如何。
supportsPictureInPicture
在清单文件的 <activity>
节点中设置此属性,以指示该 Activity 是否支持画中画模式。
<activity
android:name=".MyActivity"
android:supportsPictureInPicture=["true" | "false"] />
configChanges
要自行处理多窗口配置更改(例如,当用户调整窗口大小时),请将 android:configChanges
属性添加到应用清单的 <activity>
节点中,并至少包含以下值。
<activity
android:name=".MyActivity"
android:configChanges="screenSize | smallestScreenSize
| screenLayout | orientation" />
添加 android:configChanges
后,您的 Activity 和片段将收到对 onConfigurationChanged()
的回调,而不是被销毁和重新创建。然后,您可以根据需要手动更新视图、重新加载资源并执行其他操作。
<layout>
在 Android 7.0(API 级别 24)及更高版本中,<layout>
清单元素支持多个影响 Activity 在多窗口模式下行为的属性。
android:defaultHeight
、android:defaultWidth
:以桌面窗口模式启动 Activity 时的默认高度和宽度。android:gravity
:以桌面窗口模式启动 Activity 时的初始放置位置。请参阅Gravity
类以获取合适的属性值。android:minHeight
、android:minWidth
:在分屏和桌面窗口模式下 Activity 的最小高度和最小宽度。如果用户在分屏模式下移动分隔线以使 Activity 的大小小于指定的最小值,则系统会将 Activity 裁剪为用户请求的大小。
以下代码显示了如何在 Activity 以桌面窗口模式显示时指定其默认大小和位置以及其最小大小。
<activity android:name=".MyActivity">
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end|..."
android:minHeight="450dp"
android:minWidth="300dp" />
</activity>
运行时多窗口模式
从 Android 7.0 开始,系统提供支持可在多窗口模式下运行的应用的功能。
多窗口模式下禁用的功能
在多窗口模式下,Android 可能会禁用或忽略不适用于与其他 Activity 或应用共享设备屏幕的 Activity 的功能。
此外,某些系统 UI 自定义选项也会被禁用。例如,如果应用在多窗口模式下运行,则无法隐藏状态栏(请参阅 控制系统 UI 可见性)。
系统会忽略对 android:screenOrientation
属性的更改。
多窗口模式查询和回调
Activity
类提供以下方法来支持多窗口模式。
isInMultiWindowMode()
:指示 Activity 是否处于多窗口模式。isInPictureInPictureMode()
:指示 Activity 是否处于画中画模式。onMultiWindowModeChanged()
:每当 Activity 进入或退出多窗口模式时,系统都会调用此方法。如果 Activity 正在进入多窗口模式,则系统会向该方法传递 true 值;如果 Activity 正在退出多窗口模式,则系统会向该方法传递 false 值。onPictureInPictureModeChanged()
:每当 Activity 进入或退出画中画模式时,系统都会调用此方法。如果 Activity 正在进入画中画模式,则系统会向该方法传递 true 值;如果 Activity 正在退出画中画模式,则系统会向该方法传递 false 值。
Fragment
类公开了许多此类方法的版本;例如,Fragment.onMultiWindowModeChanged()
。
画中画模式
要将 Activity 置于画中画模式,请调用 enterPictureInPictureMode()
。如果设备不支持画中画模式,则此方法无效。有关更多信息,请参阅 使用画中画 (PiP) 添加视频。
多窗口模式下的新 Activity
启动新 Activity 时,您可以指示新 Activity 应尽可能显示在当前 Activity 旁边。使用 intent 标志 FLAG_ACTIVITY_LAUNCH_ADJACENT
,它会告诉系统尝试在相邻窗口中创建新 Activity,以便这两个 Activity 共享屏幕。系统会尽力做到这一点,但这并非保证能够实现。
如果设备处于桌面窗口模式,并且您要启动新 Activity,则可以通过调用 ActivityOptions.setLaunchBounds()
指定新 Activity 的尺寸和屏幕位置。如果设备未处于多窗口模式,则此方法无效。
在 API 级别 30 及更低版本中,如果在任务堆栈中启动 Activity,则该 Activity 将替换屏幕上的 Activity,并继承其所有多窗口属性。如果要以多窗口模式将新 Activity 作为单独的窗口启动,则必须在新的任务堆栈中启动它。
Android 12(API 级别 31)使应用能够在多个 Activity 之间拆分应用的任务窗口。您可以通过创建 XML 配置文件或进行 Jetpack WindowManager API 调用来确定应用如何显示其 Activity(全屏、并排或堆叠)。
拖放
当两个 Activity 共享屏幕时,用户可以将数据从一个 Activity 拖放到另一个 Activity。(在 Android 7.0 之前,用户只能在单个 Activity 内拖放数据。)要快速添加对接收已放置内容的支持,请参阅 DropHelper
API。有关完整的拖放指南,请参阅 启用拖放。
多实例
每个根 Activity 都有自己的任务,该任务在单独的进程中运行并在其自己的窗口中显示。要以单独的窗口启动应用的新实例,您可以使用 FLAG_ACTIVITY_NEW_TASK
标志启动新 Activity。您可以将此与一些 多窗口属性 结合使用,以请求新窗口的特定位置。例如,购物应用可以显示多个窗口来比较产品。
Android 12(API 级别 31)使您可以并排在同一任务窗口中启动 Activity 的两个实例。
如果要允许用户从应用启动器或任务栏启动应用的另一个实例,请确保您的启动器 Activity 设置了 android:resizeableActivity="true"
并且未使用阻止多个实例的启动模式。例如,当设置了 FLAG_ACTIVITY_MULTIPLE_TASK
或 FLAG_ACTIVITY_NEW_DOCUMENT
时,singleInstancePerTask
Activity 可以多次在不同的任务中实例化。
不要将多实例与多面板布局混淆,例如使用 SlidingPaneLayout
的 列表详细信息呈现,后者在单个窗口内运行。
请注意,当多个实例在可折叠设备上的单独窗口中运行时,如果姿势发生变化,则一个或多个实例可能会被发送到后台。例如,假设设备展开并且在折叠的两侧的两个窗口中运行了两个应用实例。如果设备折叠,则其中一个实例可能会被终止,而不是尝试将两个实例的窗口都适应较小的屏幕。
多窗口模式验证
无论您的应用是否以 API 级别 24 或更高版本为目标,您都应验证其在多窗口模式下的行为,以防用户尝试在运行 Android 7.0 或更高版本的设备上以多窗口模式启动它。
测试设备
运行 Android 7.0(API 级别 24)或更高版本的设备支持多窗口模式。
API 级别 23 或更低版本
当用户尝试在多窗口模式下使用应用程序时,除非应用程序声明了固定方向,否则系统会强制调整应用程序的大小。
如果您的应用程序未声明固定方向,则应在运行 Android 7.0 或更高版本的设备上启动您的应用程序,并尝试将其置于分屏模式。验证应用程序被强制调整大小时用户体验是否可以接受。
如果您的应用程序声明了固定方向,则应尝试将其置于多窗口模式。验证这样做时,应用程序是否保持全屏模式。
API 级别 24 到 30
如果您的应用程序的目标 API 级别为 24 到 30,并且未禁用多窗口支持,请在分屏和桌面窗口模式下验证以下行为
以全屏模式启动应用程序,然后通过长按最近使用的应用程序按钮切换到多窗口模式。验证应用程序是否正确切换。
直接在多窗口模式下启动应用程序,并验证应用程序是否正确启动。可以通过按最近使用的应用程序按钮,然后长按应用程序标题栏并将其拖动到屏幕上的突出显示区域之一来在多窗口模式下启动应用程序。
通过拖动屏幕分隔线在分屏模式下调整应用程序的大小。验证应用程序是否在调整大小时不会崩溃,并且必要的 UI 元素是否可见。
如果您已为应用程序指定了最小尺寸,请尝试调整应用程序的大小,使其窗口尺寸小于这些尺寸。验证您是否无法将应用程序调整为小于指定的最小尺寸。
在所有测试中,验证应用程序的性能是否可以接受。例如,验证在调整应用程序大小后更新 UI 是否没有太长的延迟。
API 级别 31 或更高版本
如果您的应用程序的目标 API 级别为 31 或更高版本,并且主活动的最小宽度和最小高度小于或等于可用显示区域的相应尺寸,请验证为API 级别 24 到 30列出的所有行为。
测试清单
要验证您的应用程序在多窗口模式下的性能,请尝试以下操作。您应该在分屏和桌面窗口模式下尝试这些操作,除非另有说明。
进入和退出多窗口模式。
从您的应用程序切换到另一个应用程序,并验证应用程序在可见但未激活时是否表现正常。例如,如果您的应用程序正在播放视频,请验证用户与其他应用程序交互时视频是否继续播放。
在分屏模式下,尝试移动屏幕分隔线以使您的应用程序变大或变小。尝试在并排和一个在上一个在下的两种配置中进行这些操作。验证应用程序不会崩溃,基本功能可见,并且调整大小操作不会花费太长时间。
连续执行多次调整大小操作。验证您的应用程序不会崩溃或内存泄漏。Android Studio 的内存分析器提供有关应用程序内存使用情况的信息(请参阅使用内存分析器检查应用程序的内存使用情况)。
在许多不同的窗口配置中正常使用您的应用程序,并验证应用程序是否表现正常。验证文本是否可读,以及 UI 元素是否不会太小而无法交互。
禁用多窗口支持
在 API 级别 24 到 30 上,如果您通过设置android:resizeableActivity="false"
禁用了多窗口支持,则应在运行 Android 7.0 到 11 的设备上启动您的应用程序,并尝试将其置于分屏和桌面窗口模式。验证这样做时,应用程序是否保持全屏模式。
其他资源
有关 Android 中多窗口支持的更多信息,请参阅
- Android MultiWindowPlayground 示例
为您推荐 * 注意:当 JavaScript 关闭时显示链接文本 * 设备兼容模式 * 支持大屏幕可调整大小 * 处理配置更改