方法指南

使用 Jetpack XR SDK 将 Androidify 引入 XR

阅读用时:9 分钟
Dereck Bridie
开发者关系工程师

Samsung Galaxy XR 隆重登场,由 Android XR 提供支持!这篇博文是我们 Android XR Spotlight Week 的一部分,在此期间,我们将提供各种资源(博文、视频、示例代码等),帮助您学习、构建和准备 Android XR 应用。

随着三星 Galaxy XR 的发布,首款由 Android XR 提供支持的设备正式亮相。现在,用户可以从 Play 商店中下载许多自己喜爱的应用,以全新的维度(3D)畅享这些应用!

第三个维度是宽敞的,可容纳大量应用。立即开始使用适合您应用的工具。例如,您可以使用 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 的移动版布局可适应各种姿势、班级规模和摄像头数量,从而提供更多照片选项。按照此模型,我们还必须调整耳机设备的相机布局。我们还需要调整文字的显示方式,以考虑界面与用户的距离。

设计适用于向全沉浸空间的更大转变

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

tablet_to_xr.webp
从平板电脑到 XR

Android 化工具使用视觉划归或窗格将功能与背景和轮廓分组,例如“拍摄或选择照片”窗格。我们还使用了顶部应用栏等组件,通过为其他窗格添加边框来营造自然的包含关系。最后,某些元素与其他元素的邻近关系暗示了内在包含关系,例如“开始转换”底部按钮靠近“选择我的机器人颜色”窗格。

空间面板,可轻松分离。如需确定如何调整移动版设计以适应空间面板,请尝试从最靠后的表面开始移除表面,然后逐步向前移动。查看您可以移除多少背景以及剩余的内容。在为 Androidify 完成这项练习后,我们最终保留了绿色的大号 Android 曲线。该波浪线不仅充当品牌宣传元素和背景,还充当 3D 空间中内容的锚点。

确定此锚点后,我们不仅可以更轻松地想象元素如何围绕它移动,还可以更轻松地想象如何利用邻近性来突破并转换其余用户体验。

其他有助于应用实现空间化的设计提示

  • 让事物不受限制:分离组件并为其提供一些真实的(空间)空间。现在是时候给这些界面元素留出一些空间了。
  • 移除表面:隐藏背景,看看这对设计有何影响。
  • 利用动态效果激发用户兴趣:您在应用中使用了哪些过渡效果?使用该角色想象您的应用进入 VR 世界。
  • 选择锚点:不要让用户在空间中迷失方向。具有可帮助收集或接地界面的元素。

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

空间界面基础知识

现在,我们已经了解了 Ivy 在为 XR 设计 Androidify 时如何调整心态,接下来我们来谈谈开发空间界面。如果您习惯使用现代 Android 工具和库,那么使用 Jetpack XR SDK 开发空间界面应该会感觉很熟悉。您会发现一些您已经熟悉的概念,例如使用 Compose 创建布局。事实上,空间布局与使用行、列和分隔器的 2D 布局非常相似:

spatialrows.png

这些元素按 SpatialRows 和 SpatialColumns 方式排列

此处显示的空间元素是 SpatialPanel 可组合项,可用于显示文本、按钮和视频等 2D 内容。

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

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

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

orbiter.png
轨道器已附加到 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)
      }
   }
}

我们只需将常规布局中的 2D 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 恤、红白格子短裤和绿色人字拖,拿着网球拍。


结果界面使用羽化效果显示免费赠送的引言,使其在屏幕边缘附近淡出。它还会在查看所用输入时使用实际的 3D 过渡,在空间中翻转图片。

发布到 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
Androidify 在 Android XR 上的 Google Play 商店中的显示效果


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

立即开始打造自己的体验

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

希望这篇博文能帮助您了解如何将自己的应用引入 Android XR!以下是一些其他链接,可帮助您顺利完成后续步骤:

作者:

继续阅读