外接显示屏可将桌面窗口化模式体验扩展到标准手机,让用户能够通过移动设备访问大屏幕。此功能为应用互动和用户工作效率开辟了新的可能性。
桌面窗口化模式的所有独特功能都适用于外接显示屏。当您将手机连接到显示屏后,手机的状态保持不变,连接的显示屏上会启动一个空白桌面会话。手机与显示屏将作为两个独立的系统运行,每个显示屏上的应用各自运行。
如果您将启用了窗口化模式的设备(例如平板电脑)连接到外接显示器,桌面会话会扩展到两个显示屏上。这两个显示屏相当于一个无缝衔接的整体。这种设置可让窗口、内容和光标在两个显示屏之间自由移动。
为了有效支持连接的显示屏,您需要注意应用设计和实现的多个方面。以下最佳实践可确保顺畅高效的用户体验。
处理动态显示变更
许多应用在构建时都假设 Display 对象及其特征在应用的生命周期内不会发生变化。不过,当用户连接或断开外部显示器,甚至在显示屏之间移动应用窗口时,与应用上下文或窗口关联的底层 Display 对象可能会改变。显示屏的属性(例如大小、分辨率、刷新率、HDR 支持和密度)可能各不相同。例如,如果您根据手机屏幕状况对值进行硬编码,则布局可能会在外部显示屏上显示异常。
外部显示屏也可能具有截然不同的像素密度。您需要确保应用能够正确响应密度变化。这涉及为布局使用密度无关像素 (dp)、提供密度特定的资源,并确保界面可适当缩放。
如果某个 activity 在外接显示屏上运行,当该显示屏断开连接时,系统会将该 activity 移至主显示屏。此移动会触发配置更改(例如屏幕尺寸和密度更改),这可能会导致重新创建 activity。您的应用必须通过保存和恢复界面状态来处理配置更改,以防止数据丢失或造成令人困惑的用户体验。
使用正确的上下文
在多显示屏环境中,使用正确的上下文至关重要。访问资源时,activity 上下文(已显示)与应用上下文(未显示)不同。
activity 上下文包含有关显示屏的信息,且始终会针对该 activity 所在的显示屏区域进行调整。这样,您就可以获取有关应用的显示密度或窗口指标的正确信息。请始终使用 activity 上下文(或其他基于界面的上下文)来获取有关当前窗口或显示屏的信息。这也会影响一些使用上下文信息的系统 API。
在 Jetpack Compose 中,您可以使用 CompositionLocal 对象(例如 LocalConfiguration.current 和 LocalDensity.current)访问显示屏专属信息。当 activity 或窗口在不同显示屏之间移动时,设备配置会发生变化,从而触发使用新的显示指标进行重组。CompositionLocal 对象可让您的界面无缝适应。
获取显示信息
您可以使用 Display 类获取显示大小、密度或标志等信息。使用 DisplayManager 系统服务获取可用的显示屏。如需识别外部显示屏,请滤除 Display.DEFAULT_DISPLAY(通常是内置手机或平板电脑屏幕):
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager val displays = displayManager.getDisplays() // The default display is 0. External displays have other IDs. val externalDisplays = displays.filter { it.displayId != Display.DEFAULT_DISPLAY }
管理 activity 启动和配置
借助连接的显示屏,应用可以指定在启动或创建其他 activity 时,应用应在哪个显示屏上运行。此行为取决于清单文件中定义的 activity 启动模式,以及启动 activity 的实体设置的 intent 标志和选项。
当 activity 移至辅助显示屏时,您的应用可能会遇到上下文更新、窗口大小调整以及配置和资源更改。如果 activity 处理配置变更,则会在 onConfigurationChanged() 中收到通知。否则,系统会重新启动该 activity。
如果为 activity 选择的启动模式允许多个实例,则在辅助屏幕上启动可能会创建该 activity 的新实例。两个 activity 会同时恢复,这对于某些多任务处理场景非常有用。
您可以使用 ActivityOptions 在特定显示屏上启动 activity。请注意,launchDisplayId 需要 Android 8(API 级别 26)或更高版本。
// Get DisplayManager and find the first external display. val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager val externalDisplayId = displayManager.displays .firstOrNull { it.displayId != Display.DEFAULT_DISPLAY } ?.displayId // If an external display is found, launch the activity on it. if (externalDisplayId != null) { val intent = Intent(this, MySecondaryActivity::class.java) val options = ActivityOptions.makeBasic() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { options.launchDisplayId = externalDisplayId } startActivity(intent, options.toBundle()) } else { // Optionally, handle the case where no external display is connected. }
避免使用设备许可清单
应用有时会通过许可清单或检查 BUILD.MODEL 和内置显示屏尺寸,将大屏界面和功能限制在特定设备上。这种方法不适用于外接显示屏,因为几乎任何设备都可以连接到大屏幕,并且在连接外接显示屏时,设备型号不会发生变化。
请勿使用许可清单或检查 BUILD.MODEL 和内置显示屏尺寸,而应在运行时检查窗口指标或设备功能,以做出界面决策。使用 Jetpack WindowManager API 或窗口大小类别,针对各种屏幕尺寸和密度构建响应式和自适应布局。
支持外部外围设备
当用户把设备连接到外部显示屏时,通常会营造一个更像桌面设备的使用环境。这常常涉及使用外接键盘、鼠标、触控板、摄像头、麦克风和音箱。您需要确保应用能与这些外围设备无缝协作。这包括处理键盘快捷键、管理鼠标指针互动、正确支持外接摄像头或麦克风,以及遵循音频输出路由。如需了解详情,请参阅大屏幕上的输入兼容性。
提高用户工作效率
连接的显示屏为提高用户工作效率提供了绝佳机会。现在,您已掌握构建移动应用所需的工具,能够打造出可与桌面应用相媲美的体验。考虑实现以下功能,以提高用户工作效率:
- 允许用户打开同一应用的多个实例。这对于比较文档、管理不同的对话或同时查看多个文件等任务非常有用。
- 让用户能够通过拖放在应用内外共享多媒体数据。
- 通过实现强大的状态管理系统,帮助用户在配置更改后保持其工作流。
通过遵循这些指南并利用提供的代码示例,您可以创建能够无缝适应连接的显示屏的应用,从而为用户提供更丰富、更高效的体验。