产品动态

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

7 分钟阅读时间
Francesco Romano
Android 开发者关系工程师

我们很高兴地宣布,在推动移动计算和桌面计算在 Android 上更紧密地结合方面,我们取得了主要里程碑:外接显示屏支持已随着 Android 16 QPR3 的发布而正式版!

如 Google I/O 2025 所示,外接显示屏允许用户将其 Android 设备连接到外接显示器,并立即访问桌面窗口化环境。应用可以在自由形式或最大化的窗口中使用,用户可以像在桌面操作系统上一样执行多项任务。

Google 和三星携手合作,为 Android 生态系统中运行 Android 16 的设备在连接到外接显示屏时提供无缝且强大的窗口化模式体验。
现在,受支持的设备* 已正式面向用户推出此功能,用户可以将受支持的 Pixel 和三星手机连接到外接显示器,从而为构建更具吸引力、更高效且可适应各种外形规格的应用体验创造新的机会。

运作方式

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

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

materialDisplay.gif

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

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

materialDisplay2.gif

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

这又有什么关联了?

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


如果您的应用是根据自适应设计原则构建的,它将自动具有桌面外观和风格,用户会感到宾至如归。如果应用锁定为纵向模式或假定仅使用触控界面,那么现在是进行现代化改造的好时机。

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

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

使用现代工具打造面向未来的桌面体验

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

新的窗口大小类:Large 和 Extra-large

Jetpack WindowManager 1.5.0 中最大的更新是新增了两个宽度窗口大小类:Large 和 Extra-large。

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

以下是新的宽度断点:

  • Large:适用于宽度介于 1200dp 和 1600dp 之间的屏幕
  • Extra-large:适用于宽度 ≥1600dp 的屏幕
windowClasses.png

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

在非常大的表面上,简单地放大平板电脑的 Expanded 布局并不总是最佳用户体验。例如,电子邮件客户端可能会在 Expanded 窗口大小类中舒适地显示两个窗格(邮箱和邮件)。但在 Extra-large 桌面显示器上,电子邮件客户端可以优雅地同时显示三个甚至四个窗格,例如邮箱、邮件列表、完整邮件内容以及日历/任务面板。

如需在项目中添加新的窗口大小类,只需从 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 版开始,默认窗格基架指令函数支持 Large 和 Extra-large 宽度窗口大小类。

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

currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)

使用入门

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

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

反馈

在不断完善外接显示屏桌面体验的过程中,您的反馈至关重要。请通过我们的 官方反馈渠道分享您的想法并报告任何问题。

我们致力于使 Android 成为一个多功能平台,能够适应用户与应用和设备互动的方式。对外接显示屏支持的改进是朝着这个方向迈出的又一步,我们相信您的用户会喜欢您构建的桌面体验!


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

作者:

继续阅读