1. 始める前に
学習内容
- XR フォーム ファクタによる、独自のユーザー エクスペリエンス。
- アプリを最大限に生かしながら Android XR ヘッドセットで動作させる方法の基礎。たとえば、Jetpack Compose for XR ライブラリで提供されるコンポーザブルを使用します。
- Compose XR ライブラリで提供される UI 要素の使い方。
- Android XR 用アプリの作成に関する情報の入手場所。
対象外:
- Compose を使用しない Android XR アプリ作成に関するガイド。Android の View ベースアプリの UI 開発を参照してください。
- Android XR 用の Unity または OpenXR アプリ作成に関するガイド。Android XR 用 Unity の開発や OpenXR での開発を参照してください。
必要なもの
- Android Studio プレビュー版(Quail Canary 1 以降)。
- Android XR エミュレータが動作する PC
- Kotlin と Jetpack Compose の使用経験(「Compose を用いた Android アプリ開発の基礎」コースを修了しているなど)
- Android Virtual Device の作成と、そこでアプリを動作させた経験
作成するアプリの概要
この Codelab では、基本的な単一画面のアプリを、Android XR を利用して没入感のあるユーザー エクスペリエンスを提供するアプリに変身させます。
|
|
出発地 | 最終結果 |
2. 設定する
コードを取得する
- この Codelab のコードは、
xr-codelabsGitHub リポジトリ内の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 の 2 つのコンセプト、スペースと空間パネルについて学びます。また、これらのコンセプトを Android XR デバイス上で動作するアプリに適用する方法についても学びます。
スペース
Android XR デバイスでは、アプリはホームスペースまたはフルスペースのいずれかで動作します。
ホームスペース

ホームスペースでは、複数のアプリを並べて実行できるので、アプリ間でマルチタスクが可能です。Android アプリは、変更することなくホームスペースで動作させることができます。
フルスペース

これらのスペースについて詳しくは、ホームスペースとフルスペースをご覧ください。
空間パネル
空間パネルはコンテナ要素で、Android XR アプリの基本的な構成要素として機能します。
ホームスペースで動作しているときは、アプリは 1 枚のパネルに含められ、大きな画面を持つ Android デバイス上のデスクトップ ウィンドウのような操作性になります。
フルスペースで動作しているときは、アプリのコンテンツを 1 つ以上のパネルに表示して、より没入感のあるエクスペリエンスを提供できます。
パネルについての詳細は空間パネルをご覧ください。
4. Android XR エミュレータでアプリを実行する
Android XR 向けにアプリの拡張を始める前に、Android XR エミュレータでアプリを実行して、ホームスペースでアプリがどのように見えるか確認できます。
Android XR システム イメージをインストールする
- まず、Android Studio で SDK Manager を開き、まだ選択していない場合は [SDK Platforms] タブを選択します。SDK Manager ウィンドウの右下にある [Show package details] チェックボックスがオンになっていることを確認します。
- Android 14 セクションで、Android XR ARM 64 v8a または Android XR Intel x86_64 のエミュレータ イメージをインストールします。イメージは、同じアーキテクチャ(x86 / ARM)のマシンでのみ実行できます。
Android XR 仮想デバイス(AVD)を作成する
- Device Manager を開き、ウィンドウの左側にある [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-alpha13"
[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 機能を使用するには、アプリをフルスペースで実行する必要があります。アプリをフルスペースにするには、次の 2 つの方法があります。
- アプリ内のユーザー インタラクションなど、プログラムを使用して移行する
- アプリのマニフェストにディレクティブを追加して、起動時にすぐに移行する
プログラムを使用してフルスペース モードにする
プログラムを使用してフルスペースにするには、アプリの UI にアフォーダンスを用意して、ユーザーが使いたいスペースを選べるようにします。また、アプリの使用状況に応じてフルスペースに移行することもできます。たとえば、動画コンテンツの視聴を始めるとフルスペースに移行し、再生が終わったら元のモードに戻ることができます。
まずは、上部のアプリバーにスペースを切り替えるボタンを追加するのが簡単でしょう。
com.example.android.xrfundamentals.ui.componentパッケージに新しいファイルToggleSpaceButton.ktを作成し、次のコンポーザブルを追加します。
ToggleSpaceButton.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 ToggleSpaceButton(
onHomeSpaceRequested: () -> Unit,
onFullSpaceRequested: () -> Unit,
modifier: Modifier = Modifier
) {
if (LocalSpatialCapabilities.current.isSpatialUiEnabled) {
ToggleSpaceButton(
modifier = modifier,
contentDescription = "Request Home Space",
iconResource = R.drawable.ic_home_space,
onClick = { onHomeSpaceRequested() }
)
} else {
ToggleSpaceButton(
modifier = modifier,
contentDescription = "Request Full Space",
iconResource = R.drawable.ic_full_space,
onClick = { onFullSpaceRequested() }
)
}
}
@Composable
fun ToggleSpaceButton(
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.LocalSpatialConfiguration
...
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun XRFundamentalsTopAppBar(
onHomeSpaceRequested: () -> Unit,
onFullSpaceRequested: () -> Unit,
) {
TopAppBar(
title = { Text(stringResource(R.string.app_name)) },
actions = {
// Only show the space toggle if the device supports spatial UI
if (LocalSpatialConfiguration.current.hasXrSpatialFeature) {
ToggleSpaceButton(onHomeSpaceRequested, onFullSpaceRequested)
}
}
)
}
XRFundamentalsApp を介して onHomeSpaceRequested と onFullSpaceRequested のインタラクションを MainActivity にホイストします。
XRFundamentalsApp.kt
import androidx.xr.compose.spatial.Subspace
...
@Composable
fun XRFundamentalsApp(
...
onHomeSpaceRequested: () -> Unit,
onFullSpaceRequested: () -> Unit,
) {
MainActivity から拡張メソッドを呼び出して、ホームスペースとフルスペースをリクエストできます。
MainActivity.kt
...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
XRFundamentalsTheme {
XRFundamentalsApp(
onHomeSpaceRequested = {
lifecycleScope.launch {
requestHomeSpace()
}
},
onFullSpaceRequested = {
lifecycleScope.launch {
requestFullSpace()
}
},
)
}
}
}
}
アプリを実行してみましょう。
|
|
アプリは起動時にホームスペースで動作します。パネル右上のボタンをタップすると、フルスペースに切り替わります。 | フルスペースで動作しているアプリです。アプリを最小化したり閉じたりするシステム UI がなくなっていることに注目してください。パネル右上のボタンをタップすると、ホームスペースに戻ります。 |
次に、注目すべき 2 つの API を簡単に説明します。
LocalSpatialConfigurationは、アプリの現在の空間設定にアクセスするためのコンポジション ローカルです。スペース変更を要求するメソッドに加えて、アプリが属するボリュームのサイズなどに関する情報も含まれています。LocalSpatialCapabilitiesは、アプリが現在どの空間機能を利用できるのか判断するために使用されるコンポジション ローカルです。スペース(ホームスペースまたはフルスペース)に加えて、空間オーディオや 3D コンテンツのサポートなどの機能も含まれています。
起動時にフルスペースにする
アクティビティをフルスペースで開始するよう 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>
これで、アプリは起動時すぐにフルスペースになります。

次に進む前に、前述の <property> 要素をマニフェストから削除してください。これによって、アプリはデフォルトの動作(ホームスペースで開く)に戻ります。
7. UI を複数のパネルに分ける
これでアプリのフルスペースの切り替えができるようになりました。次で、フルスペースをさらに活用する方法を説明します。良い活用方法の一つが、アプリのコンテンツを複数のパネルに分割して空間を覆うことです。これにより、ユーザーが必要に応じて、好きなようにパネルの移動やサイズ変更できるようになります。
アプリをサブスペースに組み込む
まず、XRFundamentalsApp コンポーザブル内にある Scaffold コンポーザブルの後に、Subspace コンポーザブルを追加します。サブスペースは、アプリ内の 3D 空間を区切ったパーティションです。これにより、3D レイアウトの構築(空間パネル追加など)、3D モデルの配置、2D コンテンツへの奥行き追加ができるようになります。
非 XR デバイスで動作するときは、Subspace コンポーザブルのコンテンツが、コンポジションに含まれることはありません。XR デバイスで動作する場合、アプリがフルスペースで動作しているときにだけ、コンテンツはコンポジションに含まれます。
XRFundamentalsApp.kt
import androidx.xr.compose.spatial.Subspace
...
HelloAndroidXRTheme {
Scaffold(...)
Subspace {
}
}
アプリを実行してみましょう。

アプリに Subspace コンポーザブルが含まれる場合は、そのコンテンツのみが表示されます。このため、ボタンをクリックしてフルスペースにすると、何も表示されなくなります。次の数ステップで 2 つの空間パネル(プライマリ コンテンツを含むパネルとセカンダリ コンテンツを含むパネル)を追加し、この問題を改善しましょう。
プライマリ コンテンツのパネルを追加する
プライマリ コンテンツをフルスペースで表示するには、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 コンポーザブルを使用しているため、パネルは 1 つの平面上ではなく、ユーザーの周りに湾曲した形で並べられ(SpatialRow の場合と同様)、ユーザーは取り囲まれたように感じます。

パネルのサイズを変更可能にする
resizable 修飾子を使ってパネルのサイズを変更できるようにすることで、ユーザーがアプリの外観を制御できるようになります。
サイズ変更可能なパネルは、デフォルトでは制限なく拡大、縮小できてしまいます。このため、含まれるコンテンツに応じて、適切に minimumSize パラメータと maximumSize パラメータを設定した方が良いでしょう。
resizable 修飾子がサポートするすべてのパラメータの詳細については、リファレンス ドキュメントをご覧ください。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.resizable
...
SpatialPanel(
modifier = SubspaceModifier
...
.resizable()
)

パネルを移動可能にする
同様に、MovePolicy を使って、パネルを移動可能にすることができます。
XRFundamentalsApp.kt
import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.movable
...
SpatialPanel(
modifier = SubspaceModifier
...
.movable()
)

MovePolicy がサポートするすべてのパラメータについて詳しくは、リファレンス ドキュメントをご覧ください。
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修飾子で利用可能な、他のパラメータも使ってみましょう。- 他のパネルを追加してみましょう。
- 空間ダイアログなどの、他の空間コンポーネントを使ってみましょう。


