产品资讯

Android 设备可无缝扩展到外接显示器

阅读用时:7 分钟
查看 Francesco Romano 的个人资料
Francesco Romano Android 开发者关系工程师

我们很高兴地宣布,Android 在拉近移动计算与桌面计算之间的距离方面取得了重大里程碑:外接显示器支持已在 Android 16 QPR3 版本中正式推出!

正如在 Google I/O 2025 上所展示的那样,外接显示器让用户能够将 Android 设备连接到外部显示器,并立即访问桌面窗口环境。应用可以在自由形式或最大化的窗口中使用,用户可以像在桌面操作系统上一样同时处理多项任务。

Google 与三星携手合作,为 Android 生态系统中运行 Android 16 的设备带来顺畅、强大的窗口化模式体验,让用户在连接外接显示屏时也能轻松操作。
此功能现已在受支持的设备* 上正式推出,用户可以将受支持的 Pixel 和三星手机连接到外部显示器,从而为打造更具吸引力、更高效的应用体验创造了新的机会,这些体验可适应各种设备规格。

运作方式

当受支持的 Android 手机或可折叠设备连接到外接显示屏时,连接的显示屏上会启动一个新的桌面会话。

连接的显示屏上的体验与桌面设备上的体验类似,包括一个显示活跃应用并允许用户固定应用以便快速访问的任务栏。用户可以在外接显示器上,在可自由调整大小的窗口中同时并排运行多个应用。

materialDisplay.gif

手机连接到外接显示屏,显示屏上显示桌面会话,而手机保持自身状态。

当支持窗口化模式的设备(例如 Samsung Galaxy Tab S11 等平板电脑)外接显示器时,桌面显示区域就会自动扩展到这两个屏幕上,从而解锁更广阔的工作区。这两个显示屏相当于一个无缝衔接的整体,应用窗口、内容和光标都可以在两屏之间自由移动。

materialDisplay2.gif

平板电脑连接到外接显示屏,将桌面会话扩展到两个显示屏。

它为何重要?

在 Android 16 QPR3 版本中,我们最终确定了窗口行为、任务栏互动和输入兼容性(鼠标和键盘),这些共同定义了外接显示器体验。我们还添加了兼容性处理,以便在切换显示屏时缩放窗口并避免应用重启。


如果您的应用是基于自适应设计原则构建的,它将自动具有桌面设备的外观和风格,让用户感到熟悉和舒适。如果应用锁定为纵向模式或采用纯触控界面,现在正是进行现代化改造的好时机。

请特别注意以下关键最佳实践,以便在外接显示屏上提供最佳应用体验:

  • 不要假定 Display 对象保持不变:当应用窗口移至外部显示屏或显示屏配置发生变化时,与应用上下文关联的 Display 对象可能会发生变化。您的应用应妥善处理配置变更事件,并动态查询显示指标,而不是缓存这些指标。
  • 考虑密度配置的变化:外部显示屏的像素密度可能与主设备屏幕大不相同。请确保您的布局和资源能够正确适应这些变化,以保持界面清晰度和易用性。为布局使用密度无关像素 (dp)、提供密度特定的资源,并确保界面可适当缩放。
  • 正确支持外部外围设备:当用户把设备连接到外部显示器时,通常会营造一个更像桌面设备的使用环境。这常常涉及使用外接键盘、鼠标、触控板、摄像头、麦克风和音箱。改进对键盘和鼠标互动的支持。

使用现代工具构建面向未来的桌面应用

我们提供了多种工具来帮助您打造桌面设备体验。让我们回顾一下核心自适应库的最新更新!

新增窗口大小类别:大和超大

Jetpack WindowManager 1.5.0 的最大更新是新增了两个宽度窗口大小类别:大和超大。

窗口大小类别是我们官方提供的一组预定义视口断点,可帮助您设计和开发自适应布局。在 1.5.0 中,我们将此指南扩展到了尺寸超出典型平板电脑的屏幕。

以下是新的宽度断点:

  • 大:适用于 1200dp 到 1600dp 之间的宽度
  • 超大:宽度 ≥1600dp
windowClasses.png

基于显示宽度的不同窗口大小类别。

在超大屏幕上,仅仅放大平板电脑的展开布局并不总是能提供最佳用户体验。例如,电子邮件客户端可以在展开窗口大小类别中轻松显示两个窗格(邮箱和邮件)。但在超大桌面显示器上,邮件客户端可以优雅地同时显示三个甚至四个窗格,例如邮箱、邮件列表、完整邮件内容和日历/任务面板。

如需在项目中添加新的窗口大小类别,只需从 WindowSizeClass.BREAKPOINTS_V2 集(而非 WindowSizeClass.BREAKPOINTS_V1)调用该函数即可:

val currentWindowMetrics =
    WindowMetricsCalculator.getOrCreate()
    .computeCurrentWindowMetrics(LocalContext.current)

val sizeClass = WindowSizeClass.BREAKPOINTS_V2
    .computeWindowSizeClass(currentWindowMetrics)

然后,在确定应用至少有该大小的空间时,应用正确的布局:

if(sizeClass.isWidthAtLeastBreakpoint(
    WindowSizeClass.WIDTH_DP_LARGE_LOWER_BOUND)){
    ...
	// Window is at least 1200 dp wide.
}

使用 Jetpack Navigation 3 构建自适应布局

Navigation 3 是 Jetpack 系列的最新成员。Navigation 3 刚刚发布了首个稳定版本,这是一个专为与 Compose 搭配使用而设计的强大导航库。

Navigation 3 也是构建自适应布局的绝佳工具,它允许同时显示多个目的地,并支持在这些布局之间无缝切换。

这个用于管理应用界面流程的系统基于场景。场景是一种同时显示一个或多个目的地的布局。SceneStrategy 决定了它是否可以创建场景。通过将 SceneStrategy 实例串联在一起,您可以针对不同的屏幕尺寸和设备配置创建并显示不同的场景。

对于列表详情和辅助窗格等开箱即用的规范布局,您可以使用 Compose Material 3 Adaptive 库中的场景(1.3 及更高版本中提供)。

您还可以通过修改场景方案或从头开始,轻松构建自己的自定义场景。例如,假设有一个场景并排显示三个窗格:

class ThreePaneScene<T : Any>(
    override val key: Any,
    override val previousEntries: List<NavEntry<T>>,
    val firstEntry: NavEntry<T>,
    val secondEntry: NavEntry<T>,
    val thirdEntry: NavEntry<T>
) : Scene<T> {
    override val entries: List<NavEntry<T>> = listOf(firstEntry, secondEntry, thirdEntry)
    override val content: @Composable (() -> Unit) = {
        Row(modifier = Modifier.fillMaxSize()) {
            Column(modifier = Modifier.weight(1f)) {
                firstEntry.Content()
            }
            Column(modifier = Modifier.weight(1f)) {
                secondEntry.Content()
            }
            Column(modifier = Modifier.weight(1f)) {
                thirdEntry.Content()
            }
        }
    }

在这种情况下,您可以定义一个 SceneStrategy,以便在窗口宽度足够大且返回堆栈中的条目已声明支持在三窗格场景中显示时,显示三个窗格。

class ThreePaneSceneStrategy<T : Any>(val windowSizeClass: WindowSizeClass) : SceneStrategy<T> {
    override fun SceneStrategyScope<T>.calculateScene(entries: List<NavEntry<T>>): Scene<T>? {
        if (windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_LARGE_LOWER_BOUND)) {
            val lastThree = entries.takeLast(3)
            if (lastThree.size == 3 && lastThree.all { it.metadata.containsKey(MULTI_PANE_KEY) }) {
                val firstEntry = lastThree[0]
                val secondEntry = lastThree[1]
                val thirdEntry = lastThree[2]


                return ThreePaneScene(
                    key = Triple(firstEntry.contentKey, secondEntry.contentKey, thirdEntry.contentKey),
                    previousEntries = entries.dropLast(3),
                    firstEntry = firstEntry,
                    secondEntry = secondEntry,
                    thirdEntry = thirdEntry
                )
            }
        }
        return null
    }
}

在创建 NavDisplay 时,您可以将 ThreePaneSceneStrategy 与其他策略搭配使用。例如,我们还可以添加一个 TwoPaneStrategy,以便在空间不足以显示三个窗格时并排显示两个窗格。

val strategy = ThreePaneSceneStrategy() then TwoPaneSceneStrategy()

NavDisplay(..., 
  sceneStrategy = strategy,
  entryProvider = entryProvider { 
    entry<MyScreen>(metadata = mapOf(MULTI_PANE_KEY to true))) { ... }
    ... other entries...
  }
)

如果空间不足,无法显示三个或两个窗格,我们的两个自定义场景策略都会返回 null。在这种情况下,NavDisplay 会回退到使用 SinglePaneScene 在单个窗格中显示返回堆栈中的最后一个条目。

通过使用场景和策略,您可以向应用添加单窗格、双窗格和三窗格布局!

adaptivepane.gif

在宽屏上显示三窗格导航的自适应应用。

如需详细了解如何使用 Navigation 3 中的场景创建自定义布局,请参阅相关文档。

独立自适应布局

如果您需要独立布局,Compose Material 3 Adaptive 库可帮助您创建自适应界面,例如列表-详情布局和辅助窗格布局,这些布局会根据窗口大小类别或设备折叠状态自动适应窗口配置。

好消息是,该库已经更新到最新断点!从版本 1.2 开始,默认窗格 scaffold 指令函数支持大宽度和超大宽度窗口大小类别。

您只需在 Gradle build 文件中声明要使用新的断点,即可选择启用:

currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)

使用入门

探索最新 Android 版本中的连接显示屏功能。在受支持的设备上获取 Android 16 QPR3,然后将其连接到外部显示器,立即开始测试您的应用!

深入了解有关多显示屏支持和窗口管理的更新文档,详细了解如何实现这些最佳实践。

反馈

在我们不断优化外接显示器桌面模式体验的过程中,您的反馈至关重要。欢迎通过我们的官方反馈渠道分享您的想法并报告任何问题。

我们致力于将 Android 打造为一个多功能平台,以适应用户与应用和设备互动的多种方式。外接显示器支持方面的改进是朝着这一方向迈出的又一步,我们相信,您打造的桌面体验一定会深受用户喜爱!


*注意:在撰写本文时,Pixel 8、9、10 系列以及各种三星设备(包括 S26、Fold7、Flip7 和 Tab S11)均支持外接显示器。

作者:
继续阅读