瞭解 Android XR 基本知識:第 1 部分 - 模式與空間面板

1. 事前準備

課程內容

  • 可透過 XR 板型規格打造的獨特使用者體驗。
  • 介紹基礎知識,瞭解如何使用 Jetpack Compose XR 程式庫提供的可組合函式調整應用程式,以便在 Android XR 頭戴式裝置上執行時發揮最大功效。
  • 如何使用 Compose XR 程式庫提供的 UI 元素。
  • 進一步瞭解如何建構 Android XR 應用程式的資源。

未提供的內容

軟硬體需求

建構項目

在本程式碼實驗室中,您將強化一個基本的單畫面應用程式,透過 Android XR 提供沈浸式使用者體驗。

起點

最終結果

2. 做好準備

取得程式碼

  1. 您可以在 xr-codelabs GitHub 存放區的 xr-fundamentals 目錄中找到本程式碼研究室的程式碼。請執行以下指令,複製這個存放區:
git clone https://github.com/android/xr-codelabs.git
  1. 或者,您也可以將存放區下載為 ZIP 檔案:

開啟專案

  • 啟動 Android Studio 後,匯入專案並只選取 xr-fundamentals/start 目錄。xr-fundamentals/part1 目錄內含解決方案程式碼;如果遇到困難,或只是想查看完整專案,都可以隨時參考。

熟悉程式碼

  • 在 Android Studio 中開啟專案後,請花點時間瀏覽範例程式碼。

3. 瞭解 XR 概念:模式與空間面板

在本程式碼研究室中,您將瞭解模式和空間面板這兩個 Android XR 概念,並學習如何在 Android XR 裝置上執行的應用程式中套用這些概念。

模式

在 Android XR 裝置上,應用程式會在首頁空間模式或完整空間模式下執行。

首頁空間模式

d779257a53898d36.jpeg

在首頁空間模式中,多個應用程式可同時執行,因此使用者可以跨應用程式多工處理。Android 應用程式可在首頁空間模式執行,不需修改程式碼。

完整空間模式

c572cdee69669a23.jpeg

在完整空間模式中,一次執行一個應用程式,沒有空間限制。所有其他應用程式都會隱藏起來。應用程式必須執行額外工作,才能進入完整空間模式,並使用此模式提供的額外功能。

如要進一步瞭解這些模式,請參閱「首頁空間和完整空間模式」相關說明。

空間面板

空間面板是容器元素,也是 Android XR 應用程式的基礎構成元素。

在首頁空間模式下執行時,應用程式會保存在單一面板中,提供類似於大螢幕 Android 裝置上的電腦分割視窗體驗。

在完整空間模式下執行時,您可以將應用程式的內容打散到一或多個面板中,提供更身歷其境的體驗。

如要進一步瞭解面板,請參閱「空間面板」相關說明。

4. 在 Android XR 模擬器中執行應用程式

開始強化這個 Android XR 應用程式之前,可以先在 Android XR 模擬器中執行應用程式,看看在首頁空間模式下執行的情形。

安裝 Android XR 系統映像檔

  1. 首先,在 Android Studio 中開啟 SDK Manager,然後選取「SDK Platforms」分頁標籤 (如果尚未選取)。在 SDK Manager 視窗的右下角,確認已勾選「Show package details」方塊。
  2. 在 Android 14 部分下方,安裝「Android XR ARM 64 v8a」或「Android XR Intel x86_64」模擬器映像檔。映像檔只能在與本身架構 (x86/ARM) 相同的機器上執行。

建立 Android XR 虛擬裝置

  1. 開啟裝置管理工具後,選取視窗左側「Category」欄下方的「XR」。接著,從清單中選取「XR Device」硬體設定檔,點選「Next」

7a5f6b9c1766d837.png

  1. 在下一頁中,選取先前安裝的系統映像檔。按一下「Next」,選取所需進階選項,最後點選「Finish」建立 AVD。
  2. 在剛剛建立的 AVD 上執行應用程式。

7cf6569ef7967d87.png

5. 設定依附元件

開始在應用程式中新增 XR 專屬功能前,您需要為 XR 程式庫新增 Jetpack Compose 的依附元件 androidx.xr.compose:compose,其中包含為應用程式打造 Android XR 最佳化體驗所需的所有可組合函式。

libs.version.toml

[versions]
...
xrCompose = "1.0.0-alpha01"

[libraries]
...
androidx-xr-compose = { group = "androidx.xr.compose", name = "compose", version.ref = "xrCompose" }

build.gradle.kts (Module :app)

dependencies {
    ...
    implementation(libs.androidx.xr.compose)
    ...
}

更新這些檔案後,請務必執行 Gradle 同步處理,確保依附元件已下載至專案。

6. 進入完整空間模式

應用程式必須在完整空間模式下執行,才能使用面板等 XR 功能。應用程式可透過以下兩種方式進入完整空間模式:

  • 透過程式輔助,例如回應應用程式中的使用者互動
  • 在應用程式資訊清單中加入指令,啟動後立即進入

透過程式輔助進入完整空間模式

如要透過程式輔助進入完整空間模式,您可以在 UI 中提供功能提示,讓使用者控制要在哪個模式中使用應用程式。此外,您也可以根據應用程式使用情境,在適當時機進入完整空間模式。例如,開始觀看影片內容時進入完整空間模式,播放完成時退出。

為簡單起見,只要在頂端應用程式列中新增一個按鈕來切換模式,就能達成這個目標。

  1. 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.IconButton
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
) {
    IconButton(
        modifier = modifier,
        onClick = onClick
    ) {
        Icon(
            painterResource(iconResource),
            contentDescription
        )
    }
}
  1. 新增按鈕,做為應用程式在 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:

  • LocalSpatialConfigurationCompositionLocal,可存取應用程式的目前空間設定。在要求變更模式的方法以外,這還包括容納應用程式的磁碟區大小等其他資訊。
  • LocalSpatialCapabilities 也是 CompositionLocal,可用來判斷應用程式目前可用哪些空間功能。除了首頁或完整空間模式以外,這還包括空間音訊和 3D 內容支援等功能。
  • LocalHasXrSpatialFeature 這個 CompositionLocal 可用來判斷執行應用程式的裝置是否支援空間 UI 功能。實際上,LocalHasXrSpatialFeature 會檢查裝置是否提供 android.software.xr.immersive 系統功能

啟動時進入完整空間模式

如要指示 OS 在完整空間模式下啟動活動,您可以在對應的 <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>

現在啟動應用程式時,會立即帶使用者進入完整空間模式。

abbf3d27cd2a4532.gif

繼續進行之前,請從資訊清單移除前述 <property> 元素,讓應用程式使用「在首頁空間模式下開啟」的預設行為。

7. 將 UI 分割到多個面板中

現在應用程式可以順利進入/退出完整空間模式,讓我們善加利用這項功能。其中一個絕佳做法是,將應用程式的內容分割到散布在空間的多個面板中,然後 (選擇性) 讓使用者根據需求移動這些面板及調整大小。

將應用程式嵌入子空間

首先,在 XRFundamentalsApp 可組合函式中,將 Subspace 可組合函式加到 Scaffold 可組合函式之後。子空間是應用程式內 3D 空間的分區,可在其中建構 3D 版面配置 (例如新增空間面板)、放置 3D 模型,以及為其他 2D 內容加上深度。

在非 XR 裝置上執行時,Subspace 可組合函式的內容一律不會進入 Composition。在 XR 裝置上,只有應用程式在完整空間模式下執行時,這些內容才會進入 Composition。

XRFundamentalsApp.kt

import androidx.xr.compose.spatial.Subspace

...

HelloAndroidXRTheme {
    Scaffold(...)
    Subspace {
    }
}

現在執行應用程式:

2d47561a616f4a11.gif

應用程式會顯示加入的 Subspace 可組合函式,用來取代 2D 內容。這表示當您點選按鈕進入完整空間模式時,再也不會出現任何內容。為修正此問題,您將在接下來的幾個步驟中新增兩個空間面板,一個容納主要內容,另一個容納次要內容。

為主要內容新增面板

Subspace 可組合函式內加入 SpatialPanel,即可在完整空間模式下顯示主要內容。

由於這是應用程式的主要面板,您可在其中加入 Scaffold,以便持續顯示頂端應用程式列內的控制項。我們將在下一個程式碼研究室中介紹軌道器,可用來空間化應用程式列通常包含的控制項,例如導覽及情境專屬動作。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialPanel

...

Subspace {
    SpatialPanel() {
        Scaffold(
            topBar = { XRFundamentalsTopAppBar() }
        ) { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                PrimaryCard(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(rememberScrollState())
                )
            }
        }
    }
}

再次執行應用程式,就會在完整空間模式下看到含主要內容的 SpatialPanel,但尺寸非常小。

89152c1991d422d4.gif

修飾主要面板

如要提高主要面板的可用性,您可以提供 SubspaceModifier 來加大面板。子空間修飾符與修飾符相似,用於修飾面板等空間元件。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.height
import androidx.xr.compose.subspace.layout.width
import androidx.compose.ui.unit.dp

...

SpatialPanel(
    modifier = SubspaceModifier
        .width(1024.dp)
        .height(800.dp)
){
    ...
}

再次執行應用程式,主要面板應該會加大許多。

c4f28838e16a3eb8.gif

為次要內容新增面板

現在應用程式已經能在完整空間模式下執行,並使用面板來顯示主要內容,該將次要內容移到其專屬面板了。請注意,必須在空間面板內使用 Surface,否則次要資訊卡就沒有背景,因為空間面板本身是透明的 (Scaffold 可組合函式已在先前步驟中處理此項目)。

XRFundamentalsApp.kt

Subspace {
    SpatialPanel() { ... }
    SpatialPanel(
        modifier = SubspaceModifier
            .width(340.dp)
            .height(800.dp)
    ) {
        Surface {
            SecondaryCardList(
                modifier = Modifier
                    .padding(16.dp)
                    .verticalScroll(rememberScrollState())
            )
        }
    }
}

現在請再次執行應用程式。乍看之下,第二個面板似乎沒有出現,但實際上有,只是隱藏在主要面板後面。

7db3c3428b64e482.gif

在列中安排面板的版面配置

如同 2D 內容的處理方式,使用列和欄來排列可組合函式,讓這些元件並排且不會互相重疊,是非常實用的做法。處理面板等空間元件時,可以使用 SpatialRowSpatialColumn 可組合函式來安排版面配置。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialRow

...

Subspace {
    SpatialRow(
        curveRadius = 825.dp
    ) {
        SpatialPanel(...) { ... }
        SpatialPanel(...) { ... }
    }
}

請再次執行應用程式,您應該會看到這兩個面板已安排在一列中,依序顯示。此外,由於有提供 curveRadiusSpatialRow,面板會在使用者左右呈弧形排列,而非放在同一個平面,可打造更身歷其境的體驗。

7455811775088baf.gif

製作可調整大小的面板

如要讓使用者控制應用程式的外觀,可以使用 resizable 子空間修飾符,製作可調整大小的面板。

根據預設,可調整大小的面板可縮小到零或無限延展,因此建議您花點時間,根據面板要容納的內容,設定適當的 minimumSizemaximumSize 參數。

如要進一步瞭解 resizable 修飾符支援的所有參數,請參閱參考說明文件

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.resizable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .resizable(true)
)

2ff2db33032fd251.gif

製作可移動的面板

同樣地,您也可以使用 movable 子空間修飾符,製作可移動的面板。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.movable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .movable(true)
)

12b6166645ea1be.gif

如要進一步瞭解 movable 修飾符支援的所有參數,請參閱參考說明文件

8. 恭喜

如要繼續瞭解如何充分發揮 XR 的效益,請參閱下列資源和練習。此外,也建議您申請參加 XR 新手上路課程

其他資訊

挑戰

  • 使用 resizablemovable 子空間修飾符適用的其他參數。
  • 新增其他面板。
  • 使用空間對話方塊等其他空間元件

參考文件