操作指南

使用 Jetpack XR SDK 将 Androidify 引入 XR

9 分钟阅读时间
2025 年 10 月 22 日
Dereck Bridie
开发者关系工程师

Samsung Galaxy XR 现已上市,由 Android XR 提供支持!这篇博文是我们 Android XR Spotlight Week 的一部分,在此期间,我们将提供各种资源(博文、视频、示例代码等),旨在帮助您学习、构建应用并为 Android XR 做好准备。

随着 Samsung Galaxy XR 的推出,首款由 Android XR 提供支持的设备正式上市。现在,用户可以在全新的维度(三维)中畅享 Play 商店中的许多常用应用!

三维空间非常宽敞,您的应用也有充足的空间。立即开始使用适合您应用的工具。例如,您可以使用Jetpack XR SDK,借助 Kotlin 和 Compose 等现代 Android 开发工具构建沉浸式 XR 体验。

在这篇博文中,我们将介绍我们如何将备受喜爱的 Androidify 应用 的奇思妙想引入 XR,并介绍将应用引入 XR 所需的基础知识。

Androidify 概览

Androidify 是一款开源应用,可让您使用 Gemini、CameraX、Navigation 3 等最新技术(当然还有 Jetpack Compose)创建 Android 机器人。Androidify 最初旨在通过创建 自适应布局,在手机、可折叠设备和平板电脑上呈现出色的效果。

customize.png

Androidify 在多种设备形态上都能呈现出色的效果

自适应布局的一个关键支柱是可重复使用的可组合项。Jetpack Compose 可帮助您创建小巧的界面组件,这些组件可以采用不同的方式布局,从而打造直观的用户体验,无论用户使用的是哪种类型的设备。事实上,Androidify 与 Android XR 兼容,无需对应用进行任何修改!

customize_2.png

Androidify 使用其大屏自适应布局来适应 XR,无需更改任何代码

对于没有针对 Android XR 进行特殊处理的应用,可以在大小合适的窗口中同时处理多个任务,并且其工作方式与在大屏设备上非常相似。因此,Androidify 在 Android XR 上已具备完整的功能,无需进行任何额外的工作!但我们并不满足于此,因此决定再接再厉,打造一款 XR 差异化应用,为我们的 XR 用户带来愉悦的体验。

在 XR 中确定方向

我们先来了解 Android XR 的关键基本概念,首先是应用可以在其中运行的两种模式:主共享空间和全沉浸空间。

homespace.png
主共享空间中的应用
homespace2.png
全沉浸空间中的应用

在 主共享空间 中,多个应用可以并排运行,以便用户在不同的窗口之间同时处理多个任务。从这个意义上讲,它很像大屏 Android 设备上的窗口化模式,但位于虚拟空间中!

全沉浸空间 中,应用没有空间边界,可以充分利用 Android XR 的全空间功能,例如空间界面和控制虚拟环境。

虽然让应用仅在全沉浸空间中运行可能很诱人,但用户可能希望同时处理多个任务,因此同时支持这两种模式可以提升用户体验。

为 Androidify 的新维度设计

愉悦的应用始于出色的设计。Android DevRel 的高级设计倡导者 Ivy Knight 承担了为 Androidify 打造新 XR 设计的任务。下面有请 Ivy!

为 XR 设计需要采用独特的方法,但实际上与移动设计有很多共同之处。我们首先考虑的是包含:如何在子空间中组织和分组界面元素,方法是明确显示边界或巧妙地暗示边界。我们还学会了接受各种大小的空间界面元素,这些元素旨在根据用户进行调整和移动。与 Androidify 一样,使用自适应布局进行构建,以便将布局分解为空间界面的各个部分。

从主共享空间开始设计

幸运的是,Android XR 可让您从应用当前的“主共享空间”开始,因此我们只需添加窗口工具栏和“全沉浸空间”过渡按钮,即可过渡到扩展的 XR 设计。

我们还考虑了可能的硬件功能以及用户与这些功能的互动方式。Androidify 的移动布局可适应各种姿势、班级规模和摄像头数量,从而提供更多照片选项。按照此模型,我们还必须针对头戴设备调整摄像头布局。我们还需要针对文本进行调整,以考虑界面与用户的距离

为向 Full Space 的更大转变而设计

全沉浸空间 是最大的转变,但为我们提供了最大的创意空间来调整设计。

tablet_to_xr.webp
从平板电脑到 XR

Androidify 使用视觉包含(即窗格)将具有背景和轮廓的功能分组,例如“拍摄或选择照片”窗格。我们还使用了顶部应用栏等组件,通过为其他窗格添加边框来创建自然包含。最后,某些元素之间的距离暗示了内在包含,例如“开始转换”底部按钮,该按钮靠近“选择我的机器人颜色”窗格。

空间面板便于分离。如需确定如何针对空间面板调整移动设计,请尝试移除表面,从最靠后的表面开始,然后向前移动。查看可以移除多少背景以及剩余的内容。在为 Androidify 完成此练习后,剩余的是一个大型绿色 Android 曲线。该曲线不仅充当品牌宣传时刻和背景,还充当三维空间中内容的锚点。

建立此锚点不仅更容易想象元素如何围绕它移动,还更容易想象如何使用距离来分解和转换其余的用户体验。

其他设计提示,帮助您的应用实现空间化

  • 让事物不受限制:分解组件并为它们提供一些真实的(空间)空间。现在是时候为这些界面元素提供一些呼吸空间了。
  • 移除表面:隐藏背景,看看这对设计有何影响。
  • 通过动画激发灵感:您在应用中如何使用过渡?使用该角色想象您的应用进入 VR。
  • 选择锚点:不要让用户在空间中迷失。使用有助于收集或固定界面的元素。

如需详细了解 XR 界面设计模式,请参阅 Android 开发者网站上的 Android XR 设计

空间界面基础知识

现在,我们已经介绍了 Ivy 在为 XR 设计 Androidify 时调整思维方式的体验,接下来我们来讨论开发空间界面。如果您习惯使用现代 Android 工具和库,那么使用 Jetpack XR SDK 开发空间界面应该会很熟悉。您会发现一些已经熟悉的概念,例如使用 Compose 创建布局。事实上,空间布局与使用行、列和间距的二维布局非常相似:

spatialrows.png

这些元素排列在 SpatialRows 和 SpatialColumns

此处显示的空间元素是 SpatialPanel 可组合项,可让您显示文本、按钮和视频等二维内容。

  Subspace {
    SpatialPanel(
        SubspaceModifier
            .height(824.dp)
            .width(1400.dp)
    ) {
        Text("I'm a panel!")
    }
}

SpatialPanel 是 子空间可组合项。子空间可组合项必须包含在子空间内,并由 SubspaceModifier 对象进行修改。子空间可以放置在应用界面层次结构的任何位置,并且只能包含子空间可组合项。SubspaceModifier 对象与 Modifier 对象也非常相似:它们控制大小和位置等参数。

一个 Orbiter 可以附加到 SpatialPanel,并随其附加的内容一起移动。它们通常用于提供有关其附加内容的上下文控件,从而使内容成为主要焦点。它们可以放置在内容的任意四侧,距离可配置。

orbiter.png
Orbiter 附加到 SpatialPanel 的底部

还有许多其他空间界面元素,但这些是我们用于为 Androidify 创建空间布局的主要元素。

XR 开发入门

我们先从项目设置开始。我们添加了 Jetpack XR Compose 依赖项,您可以在 Jetpack XR 依赖项 页面上找到该依赖项。

我们为将用户过渡到全沉浸空间的按钮添加了代码,首先检测是否具备此功能:

  @Composable
fun couldRequestFullSpace(): Boolean =
   LocalSpatialConfiguration.current.hasXrSpatialFeature && 
   !LocalSpatialCapabilities.current.isSpatialUiEnabled
}

然后,我们为现有布局创建了一个新的按钮组件,该组件使用“展开内容”图标,并为其添加了 onClick 行为:

  @Composable

fun RequestFullSpaceIconButton() {
   if (!couldRequestFullSpace()) return
   val session = LocalSession.current ?: return

   IconButton(
       onClick = {
           session.scene.requestFullSpaceMode()
       },
   ) {
       Icon(
           imageVector =  
               vectorResource(R.drawable.expand_content_24px),
           contentDescription = 
               stringResource("To Full Space"),
       )
   }
}

现在,点击该按钮只会显示全沉浸空间中的“中”布局。我们可以检查空间功能,并确定是否可以显示空间界面 - 如果可以,我们将显示新的空间布局:

  @Composable

fun HomeScreenContents(layoutType: HomeScreenLayoutType) {
   val layoutType = when {
      LocalSpatialCapabilities.current.isSpatialUiEnabled -> 
          HomeScreenLayoutType.Spatial
      isAtLeastMedium() -> HomeScreenLayoutType.Medium
      else -> HomeScreenLayoutType.Compact
   }

   when (layoutType) {
      HomeScreenLayoutType.Compact ->
          HomeScreenCompactPager(...)

      HomeScreenLayoutType.Medium ->
          HomeScreenMediumContents(...)

      HomeScreenLayoutType.Spatial ->
          HomeScreenContentsSpatial(...)
   }
}

实现主屏幕的设计

我们回到全沉浸空间中主屏幕的空间设计,了解其实现方式。

customize_3.png

我们在此处确定了两个 SpatialPanel 元素:右侧视频卡片所在的面板,以及包含主界面的面板。最后,顶部附加了一个 Orbiter。我们先从视频播放器面板开始:

  @Composable
fun HomeScreenContentsSpatial(...) {
   Subspace {
      SpatialPanel(SubspaceModifier
                   .fillMaxWidth(0.2f)
                   .fillMaxHeight(0.8f)
                   .aspectRatio(0.77f)
                   .rotate(0f, 0f, 5f),
      ) {
          VideoPlayer(videoLink)
      }
   }
}

我们只需将常规布局中的二维 VideoPlayer 组件重复使用到 SpatialPanel 中,无需进行任何其他更改!下面是其单独显示时的样子:

bluetiel.png

主内容面板也遵循相同的模式:我们在 SpatialPanel 中重复使用了中面板内容。

  SpatialPanel(SubspaceModifier.fillMaxSize(),
             resizePolicy = ResizePolicy(
                 shouldMaintainAspectRatio = true
             ),
             dragPolicy = MovePolicy()
) {
    Box {
        FillBackground(R.drawable.squiggle_full)
        HomeScreenSpatialMainContent(...)
    }
}

我们为此面板添加了 ResizePolicy,该政策为面板提供了一些靠近边缘的句柄,让用户可以调整面板的大小。它还具有 MovePolicy,可让用户拖动面板。

customize_4.png

将它们放置在同一子空间中会使它们彼此独立,因此我们将 VideoPlayer 面板设为主内容面板的子项。这样一来,当主内容面板通过父子关系被拖动时,VideoPlayer 面板也会随之移动。

  @Composable
fun HomeScreenContentsSpatial(...) {
   Subspace {
       SpatialPanel(SubspaceModifier..., resizePolicy, dragPolicy) {
           Box {
               FillBackground(R.drawable.squiggle_full)
               HomeScreenSpatialMainContent(...)
           }
           Subspace {
              SpatialPanel(SubspaceModifier...) {
                  VideoPlayer(videoLink)
              }
           }
       }
   }
}

这就是我们完成第一个屏幕的方式!

接下来介绍其他屏幕

我还会简要介绍其他一些屏幕,重点介绍针对每个屏幕所做的具体考虑。

fullspace.png
全沉浸空间中的创建屏幕

在这里,我们使用了 SpatialRow 和 SpatialColumn 可组合项来创建适合推荐观看空间的布局,同样重复使用了中布局中的组件。

fullspace_2.png

全沉浸空间 中的结果屏幕:根据提示生成的机器人:红色棒球帽、飞行员太阳镜、浅蓝色 T 恤、红白格子短裤、绿色人字拖,并拿着网球拍。


结果屏幕使用羽化效果显示补充引用,使其在屏幕边缘附近逐渐消失。它还在查看所用输入时使用实际的三维过渡,在空间中翻转图片。

发布到 Google Play 商店

现在,应用已准备好在 XR 中使用空间布局,接下来我们将其发布到 Play 商店。我们对应用的 AndroidManifest.xml 文件进行了一项重要的最终更改:

  <!-- Androidify can use XR features if they're available; they're not required. -->
<uses-feature android:name="android.software.xr.api.spatial" 
              android:required="false" />

这样一来,Play 商店就知道此应用具有 XR 差异化功能,并显示一个徽章,让用户知道该应用是专门为 XR 设计的:

androidify2.png
Android XR 上 Google Play 商店中显示的 Androidify


上传发布版本时,我们无需执行任何特殊步骤即可为 XR 发布:与移动轨道上的用户一样,同一应用也会正常分发给 XR 设备上的用户!不过,您可以选择添加应用的 XR 专用屏幕截图,甚至可以使用空间视频素材资源上传应用的沉浸式预览。在 Android XR 设备上,Play 商店会自动将其显示为沉浸式三维预览,让用户在安装应用之前体验内容的深度和规模。

立即开始构建自己的体验

Androidify 是一个很好的示例,展示了如何将现有的二维 Jetpack Compose 应用空间化。今天,我们展示了为 Androidify 开发空间界面的完整过程,从设计到代码再到发布。我们修改了现有设计以适应空间范式,使用 SpatialPanel 和 Orbiter 可组合项创建了在用户进入全沉浸空间时显示的空间布局,最后将新版应用发布到了 Play 商店。

希望这篇博文能帮助您了解如何将自己的应用引入 Android XR!以下是一些链接,可帮助您入门:

作者:

继续阅读