1. 开始之前
您将学习什么
- XR 外形设备所实现的独特用户体验。
- 如何通过使用 Jetpack Compose XR 库提供的可组合项,将应用进行调整以充分利用 Android XR 头戴设备的功能。
- 如何使用 Compose XR 库提供的 UI 元素。
- 在哪里可以了解更多关于为 Android XR 构建应用的信息。
这不是什么
- 这不是一个不使用 Compose 构建 Android XR 应用的指南。请参阅为基于 Android Views 的应用开发 UI。
- 这不是一个为 Android XR 构建 Unity 或 OpenXR 应用的指南。请参阅使用 Unity 为 Android XR 开发和使用 OpenXR 开发。
您需要什么
- Android Studio 预览版
- 一台能够运行 Android XR 模拟器的计算机
- 具有 Kotlin 和 Jetpack Compose 经验,例如,通过完成使用 Compose 学习 Android 基础知识课程
- 具有创建 Android 虚拟设备和在其上运行应用的经验。
您将构建什么
在此 Codelab 中,您将增强一个基本的单屏应用,以通过 Android XR 提供沉浸式用户体验。
起点 | 最终结果 |
2. 设置
获取代码
- 此 Codelab 的代码可在
xr-codelabs
GitHub 仓库中的xr-fundamentals
目录中找到。要克隆仓库,请运行以下命令
git clone https://github.com/android/xr-codelabs.git
- 或者,您可以将仓库下载为 ZIP 文件
打开项目
- 启动 Android Studio 后,导入项目,仅选择
xr-fundamentals/start
目录。xr-fundamentals/part1
目录包含解决方案代码,您可以在任何时候卡住或只是想查看完整项目时参考它。
熟悉代码
- 在 Android Studio 中打开项目后,花一些时间查看起始代码。
3. 学习 XR 概念:模式和空间面板
在此 Codelab 中,您将学习两个 Android XR 概念:模式和空间面板。您还将学习如何将这些概念应用于在 Android XR 设备上运行的应用。
模式
在 Android XR 设备上,应用以两种模式之一运行:主空间模式或全空间模式。
主空间模式
在主空间模式下,多个应用并排运行,因此用户可以在应用之间进行多任务处理。Android 应用无需修改即可在主空间模式下运行。
全空间模式
在全空间模式下,每次只有一个应用运行,没有空间边界。所有其他应用都被隐藏。应用必须进行额外的工作才能进入全空间模式,并利用此模式下可用的额外功能。
要了解有关这些模式的更多信息,请参阅主空间和全空间模式
空间面板
空间面板是容器元素,充当 Android XR 应用的基本构建块。
在主空间模式下运行时,您的应用将包含在一个面板中,提供类似于在大屏幕 Android 设备上桌面窗口化的体验。
在全空间模式下运行时,您可以将应用内容分解为一个或多个面板,以提供更沉浸的体验。
要了解有关面板的更多信息,请参阅空间面板。
4. 在 Android XR 模拟器中运行应用
在开始为 Android XR 增强应用之前,您可以在 Android XR 模拟器中运行应用,以查看其在主空间模式下的外观。
安装 Android XR 系统映像
- 首先,在 Android Studio 中打开 SDK 管理器,如果尚未选择,请选择SDK Platforms选项卡。在 SDK 管理器窗口的右下角,确保选中了Show package details(显示软件包详细信息)框。
- 在 Android 14 部分下,安装 Android XR ARM 64 v8a 或 Android XR Intel x86_64 模拟器映像。映像只能在与自身具有相同架构(x86/ARM)的机器上运行。
创建 Android XR 虚拟设备
- 打开设备管理器后,在窗口左侧的Category(类别)列下选择XR。然后,从列表中选择XR Device硬件配置文件,然后点击Next(下一步)。
- 在下一页上,选择您之前安装的系统映像。点击Next(下一步),并选择您想要的任何高级选项,最后通过点击Finish(完成)创建 AVD。
- 在您刚刚创建的 AVD 上运行应用。
5. 设置依赖项
在开始向应用添加 XR 特定功能之前,您需要添加对 Jetpack Compose for XR 库 androidx.xr.compose:compose
的依赖项,该库包含构建 Android XR 差异化体验所需的所有可组合项。
libs.version.toml
[versions]
...
xrCompose = "1.0.0-alpha04"
[libraries]
...
androidx-xr-compose = { group = "androidx.xr.compose", name = "compose", version.ref = "xrCompose" }
build.gradle.kts(模块:app)
dependencies {
...
implementation(libs.androidx.xr.compose)
...
}
更新这些文件后,请务必进行Gradle 同步,以确保依赖项已下载到您的项目中。
6. 进入全空间模式
要使用面板等 XR 功能,应用必须在全空间模式下运行。应用进入全空间模式有两种方式
- 以编程方式,例如响应应用中的用户交互
- 在启动时立即通过向应用清单添加指令。
以编程方式进入全空间模式
要以编程方式进入全空间模式,您可以在 UI 中提供辅助功能,让用户控制他们希望以哪种模式使用您的应用。此外,当在您的应用使用上下文中合理时,您也可以进入全空间模式。例如,在开始观看视频内容时进入全空间模式,并在播放完成后退出。
为了简化,这可以通过首先在顶部应用栏中添加一个按钮来切换模式来实现。
- 在
com.example.android.xrfundamentals.ui.component
包中创建一个新文件ToggleSpaceModeButton.kt
并添加以下可组合项
ToggleSpaceModeButton.kt
package com.example.android.xrfundamentals.ui.component
import androidx.annotation.DrawableRes
import androidx.compose.material3.Icon
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.xr.compose.platform.LocalSpatialCapabilities
import androidx.xr.compose.platform.LocalSpatialConfiguration
import com.example.android.xrfundamentals.R
import com.example.android.xrfundamentals.ui.theme.XRFundamentalsTheme
@Composable
fun ToggleSpaceModeButton(modifier: Modifier = Modifier) {
val spatialConfiguration = LocalSpatialConfiguration.current
if (LocalSpatialCapabilities.current.isSpatialUiEnabled) {
ToggleSpaceModeButton(
modifier = modifier,
contentDescription = "Request Home Space mode",
iconResource = R.drawable.ic_home_space_mode,
onClick = { spatialConfiguration.requestHomeSpaceMode() }
)
} else {
ToggleSpaceModeButton(
modifier = modifier,
contentDescription = "Request Full Space mode",
iconResource = R.drawable.ic_full_space_mode,
onClick = { spatialConfiguration.requestFullSpaceMode() }
)
}
}
@Composable
fun ToggleSpaceModeButton(
contentDescription: String,
@DrawableRes iconResource: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
FilledTonalIconButton(
modifier = modifier,
onClick = onClick
) {
Icon(
painterResource(iconResource),
contentDescription
)
}
}
- 当应用在 XR 设备上运行时,将按钮添加为
TopAppBar
中的操作
XRFundamentalsTopAppBar.kt
import androidx.xr.compose.platform.LocalHasXrSpatialFeature
...
TopAppBar(
...,
actions = {
// Only show the mode toggle if the device supports spatial UI
if (LocalHasXrSpatialFeature.current) {
ToggleSpaceModeButton()
}
}
)
现在运行应用。
应用启动时在主空间模式下运行。点击面板右上角的按钮切换到全空间模式。 | 应用在全空间模式下运行。请注意,最小化/关闭应用的系统 UI 已消失。点击面板右上角的按钮切换回主空间模式。 |
这些代码片段包含一些值得注意的新 API
LocalSpatialConfiguration
是一个组合本地,提供对应用当前空间配置的访问。除了请求更改模式的方法外,这还包括其他信息,例如包含应用的体积大小。LocalSpatialCapabilities
是一个组合本地,可用于确定应用当前可用的空间能力。除了模式(主空间或全空间)外,这还包括空间音频和 3D 内容支持等能力。LocalHasXrSpatialFeature
是一个组合本地,可用于确定应用是否在支持空间 UI 功能的设备上运行。它在底层检查设备是否具有android.software.xr.immersive
系统功能。
在启动时进入全空间模式
要指示操作系统以全空间模式启动 Activity,您可以在相应的 <activity>
元素中包含一个具有以下属性的 <property>
元素。仅当用户不太可能同时使用您的应用和其他应用时才建议这样做。
AndroidManifest.xml
<activity
android:name=".MainActivity"
... >
<property
android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />
</activity>
现在,当应用启动时,用户会立即进入全空间模式。
在继续之前,请从清单中删除上述 <property>
元素,以便应用使用在主空间模式下打开的默认行为。
7. 将 UI 拆分为多个面板
现在您的应用可以进入和退出全空间模式了,是时候更好地利用它了。一个很好的方法是将应用内容拆分为多个面板以填充空间,并(可选地)允许用户根据需要移动和调整这些面板的大小。
将您的应用嵌入子空间
首先,在 XRFundamentalsApp
可组合项中的 Scaffold
可组合项之后添加一个 Subspace
可组合项。子空间是您应用中 3D 空间的一个分区,您可以在其中构建 3D 布局(例如添加空间面板)、放置 3D 模型,并为 2D 内容添加深度。
在非 XR 设备上运行时,Subspace
可组合项的内容永远不会进入 Composition。在 XR 设备上运行时,只有当应用在全空间模式下运行时,内容才会进入 Composition。
XRFundamentalsApp.kt
import androidx.xr.compose.spatial.Subspace
...
HelloAndroidXRTheme {
Scaffold(...)
Subspace {
}
}
现在,运行应用
当您的应用包含 Subspace
可组合项时,只会显示其内容。这意味着当您点击按钮进入全空间模式时,不再显示任何内容。要解决此问题,您将在接下来的几个步骤中添加两个空间面板,一个用于包含主要内容,另一个用于次要内容。
为主要内容添加面板
要在全空间模式下显示主要内容,请在 Subspace
可组合项中添加一个 SpatialPanel
。
由于这是应用的主面板,您可以将 Scaffold
包含在其中,以保持顶部应用栏中的控件存在。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.SpatialPanel
import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.height
import androidx.xr.compose.subspace.layout.width
...
Subspace {
SpatialPanel(
modifier = SubspaceModifier
.width(1024.dp)
.height(800.dp)
) {
Scaffold(
topBar = { XRFundamentalsTopAppBar() }
) { innerPadding ->
Box(Modifier.padding(innerPadding)) {
PrimaryCard(
modifier = Modifier
.padding(16.dp)
.verticalScroll(rememberScrollState())
)
}
}
}
}
默认情况下,SpatialPanel
的大小是根据其包含的内容大小确定的,但也可以通过提供 SubspaceModifier
参数来确定。子空间修饰符类似于修饰符,用于修改空间布局组件,如面板。
因为此空间面板的内容未指定其自身的大小,所以必须为面板提供一个 SubspaceModifier
以使其具有宽度和高度。
为次要内容添加面板
现在您已经让应用在全空间模式下运行并使用面板显示主要内容,是时候将次要内容移动到其自己的面板中了。请注意在空间面板中使用 Surface
。如果没有它,次要卡片将没有背景,因为空间面板本身是透明的(Scaffold
可组合项在前面的步骤中处理了这一点)。
XRFundamentalsApp.kt
import androidx.compose.material3.Surface
...
Subspace {
SpatialPanel() { ... }
SpatialPanel(
modifier = SubspaceModifier
.width(340.dp)
.height(800.dp)
) {
Surface {
SecondaryCardList(
modifier = Modifier
.padding(16.dp)
.verticalScroll(rememberScrollState())
)
}
}
}
现在再次运行应用程序。乍一看,第二个面板可能没有显示出来,但它实际上是显示的——只是被主面板隐藏了。
将面板排列成一行
与 2D 内容一样,使用行和列有助于并排排列可组合项而不会重叠。在使用面板等空间组件时,您可以使用 SpatialRow
、SpatialCurvedRow
和 SpatialColumn
可组合项来实现此目的。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.SpatialCurvedRow
...
Subspace {
SpatialCurvedRow(
curveRadius = 825.dp
) {
SpatialPanel(...) { ... }
SpatialPanel(...) { ... }
}
}
再运行一次应用程序,您应该会看到面板一个接一个地排列成一行。
因为您使用的是 SpatialCurvedRow
可组合项,所以面板会围绕用户弯曲而不是保持在同一平面(就像 SpatialRow
的情况一样),从而提供更具包围感的体验。
使面板可调整大小
为了让用户控制应用的外观,您可以使用 resizable
子空间修饰符使面板可调整大小。
默认情况下,可调整大小的面板可以缩小到零或无限扩展,因此您可能需要花时间根据它们将包含的内容设置适当的 minimumSize
和 maximumSize
参数。
有关 resizable
修饰符支持的所有参数的更多详细信息,请参阅参考文档。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.layout.resizable
...
SpatialPanel(
modifier = SubspaceModifier
...
.resizable(true)
)
使面板可移动
同样,您可以使用 movable
子空间修饰符使面板可移动。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.layout.movable
...
SpatialPanel(
modifier = SubspaceModifier
...
.movable(true)
)
有关 movable
修饰符支持的所有参数的更多详细信息,请参阅参考文档。
8. 更新清单
现在您已经为应用添加了空间功能,您需要更新清单以表明您的应用支持这些功能。这让 Google Play 可以将您的应用突出显示为 Android XR 设备的差异化应用。
为此,请在 AndroidManifest.xml
文件中添加以下 <uses-feature>
元素
AndroidManifest.xml
<application ...>
...
<uses-feature android:name="android.software.xr.api.spatial" android:required="false"/>
...
</application>
将 android:required
属性的值设为 false
表示如果该功能可用,应用会使用它,但不会阻止应用分发到没有该功能的设备(例如手机)。这使您可以从移动发布轨道分发到手机和 XR 设备。如果您想使用 XR 专用轨道,请将 android:required
设置为 true
。
9. 恭喜
要继续学习如何充分利用 Android XR,请查看下一个 Codelab:学习 Android XR 基础知识:第 2 部分 - 轨道器和空间环境,以及以下资源和练习
进一步阅读
- XR 设计涵盖了为 Android XR 构建应用时要使用的设计原则和最佳实践。
- 使用 Jetpack XR SDK 进行开发包含有关您可以在构建 Android XR 体验中使用的 API 和工具的技术指导。
- Android XR 应用质量指南页面描述了创建出色用户体验的标准。
- 深入了解Hello Android XR 示例
挑战
- 使用
resizable
和movable
子空间修饰符的附加参数。 - 添加更多面板。
- 使用其他空间组件,例如空间对话框