XR 向け Jetpack Compose で UI を開発する

Jetpack Compose for XR を使用すると、行や列などの使い慣れた Compose のコンセプトを使用して、空間 UI とレイアウトを宣言的に構築できます。これにより、既存の Android UI を 3D 空間に拡張したり、まったく新しい没入型 3D アプリを構築したりできます。

既存の Android ビューベースのアプリを空間化する場合は、いくつかの開発オプションがあります。相互運用 API を使用するか、Compose とビューを併用するか、SceneCore ライブラリを直接使用できます。詳しくは、ビューの操作に関するガイドをご覧ください。

サブスペースと空間化されたコンポーネントについて

Android XR 用アプリを作成する場合は、サブスペース空間化されたコンポーネントの概念を理解することが重要です。

サブスペースについて

Android XR 向けに開発する場合は、アプリまたはレイアウトにサブスペースを追加する必要があります。サブスペースは、アプリ内の 3D 空間のパーティションです。3D コンテンツを配置したり、3D レイアウトを作成したり、2D コンテンツに奥行きを追加したりできます。サブスペースは、空間化が有効になっている場合にのみレンダリングされます。ホーム スペースまたは XR 以外のデバイスでは、そのサブスペース内のコードは無視されます。

サブスペースを作成するには、次の 2 つの方法があります。

  • setSubspaceContent: この関数は、アプリレベルのサブスペースを作成します。これは、setContent を使用する場合と同じ方法で MainActivity で呼び出すことができます。アプリレベルのサブスペースは高さ、幅、奥行きに制限がなく、基本的に空間コンテンツ用の無限のキャンバスを提供します。
  • Subspace: このコンポーザブルはアプリの UI 階層内の任意の場所に配置できるため、ファイル間のコンテキストを失うことなく、2D と空間 UI のレイアウトを維持できます。これにより、UI ツリー全体で状態をホイスティングしたり、アプリを再設計したりすることなく、XR と他のフォーム ファクタ間で既存のアプリ アーキテクチャなどを簡単に共有できます。

詳細については、アプリにサブスペースを追加するをご覧ください。

空間化されたコンポーネントについて

サブスペース コンポーザブル: これらのコンポーネントはサブスペースでのみレンダリングできます。2D レイアウト内に配置する前に、Subspace または setSubspaceContent で囲む必要があります。SubspaceModifier を使用すると、サブスペース コンポーザブルに深度、オフセット、配置などの属性を追加できます。

  • サブスペース修飾子に関する注意: SubspaceModifier API の順序に注意してください。
    • オフセットは修飾子チェーンで最初に指定する必要があります
    • 移動可能とサイズ変更可能は最後に記述する必要があります
    • 回転はスケールの前に適用する必要があります

他の空間化されたコンポーネントは、サブスペース内で呼び出す必要はありません。空間コンテナ内にラップされた従来の 2D 要素で構成されています。これらの要素は、両方に定義されている場合は、2D または 3D レイアウト内で使用できます。空間化が有効になっていない場合、空間化された特徴は無視され、2D の対応する特徴にフォールバックします。

空間パネルを作成する

SpatialPanel は、アプリ コンテンツを表示できるサブスペース コンポーザブルです。たとえば、動画の再生、静止画像、その他のコンテンツを空間パネルに表示できます。

空間 UI パネルの例

次の例に示すように、SubspaceModifier を使用して空間パネルのサイズ、動作、配置を変更できます。

Subspace {
   SpatialPanel(
        SubspaceModifier
           .height(824.dp)
           .width(1400.dp)
           .movable()
           .resizable()
           ) {
          SpatialPanelContent()
      }
}

// 2D content placed within the spatial panel
@Composable
fun SpatialPanelContent(){
    Box(
        Modifier
            .background(color = Color.Black)
            .height(500.dp)
            .width(500.dp),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "Spatial Panel",
            color = Color.White,
            fontSize = 25.sp
        )
    }
}

コードに関する主なポイント

  • サブスペース修飾子に関する注意: SubspaceModifier API の順序に注意してください。
    • オフセットは修飾子チェーンで最初に指定する必要があります。
    • 移動可能およびサイズ変更可能な修飾子は最後に指定する必要があります。
    • 回転はスケーリングの前に適用する必要があります。
  • SpatialPanel API はサブスペース コンポーザブルであるため、Subspace または setSubspaceContent 内で呼び出す必要があります。サブスペースの外部で呼び出すと、例外がスローされます。
  • .movable または .resizable SubspaceModifier を追加して、ユーザーがパネルのサイズ変更や移動を行えるようにします。
  • サイズと配置について詳しくは、空間パネルの設計に関するガイダンスをご覧ください。コード実装の詳細については、リファレンス ドキュメントをご覧ください。

オビッターを作成する

オービターは空間 UI コンポーネントです。対応する空間パネルに接続するように設計されており、その空間パネルに関連するナビゲーション アイテムとコンテキスト アクション アイテムが含まれています。たとえば、動画コンテンツを表示する空間パネルを作成した場合は、オービター内に動画再生コントロールを追加できます。

オルビター(軌道上を周回する宇宙船)の例

次の例に示すように、SpatialPanel 内でオービターを呼び出して、ナビゲーションなどのユーザー コントロールをラップします。これにより、2D レイアウトから抽出され、構成に応じて空間パネルに接続されます。

setContent {
    Subspace {
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
                .movable()
                .resizable()
        ) {
            SpatialPanelContent()
            OrbiterExample()
        }
    }
}

//2D content inside Orbiter
@Composable
fun OrbiterExample() {
    Orbiter(
        position = OrbiterEdge.Bottom,
        offset = 96.dp,
        alignment = Alignment.CenterHorizontally
    ) {
        Surface(Modifier.clip(CircleShape)) {
            Row(
                Modifier
                    .background(color = Color.Black)
                    .height(100.dp)
                    .width(600.dp),
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = "Orbiter",
                    color = Color.White,
                    fontSize = 50.sp
                )
            }
        }
    }
}

コードに関する主なポイント

  • サブスペース修飾子に関する注意事項: SubspaceModifier API の順序に注意してください。
    • オフセットは修飾子チェーンで最初に指定する必要があります
    • 移動可能とサイズ変更可能は最後に記述する必要があります
    • 回転はスケールの前に適用する必要があります
  • オビターは空間 UI コンポーネントであるため、コードは 2D または 3D レイアウトで再利用できます。2D レイアウトでは、アプリはオービター内のコンテンツのみをレンダリングし、オービター自体は無視されます。
  • オビッターを使用する方法と設計方法について詳しくは、設計に関するガイダンスをご覧ください。

空間レイアウトに複数の空間パネルを追加する

SpatialRowSpatialColumnSpatialBoxSpatialLayoutSpacer を使用して、複数の空間パネルを作成し、空間レイアウト内に配置できます。

空間レイアウト内の複数の空間パネルの例

次のコードサンプルは、これを行う方法を示しています。

Subspace {
    SpatialRow {
        SpatialColumn {
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Top Left")
            }
            SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
                SpatialPanelContent("Middle Left")
            }
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Bottom Left")
            }
        }
        SpatialColumn {
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Top Right")
            }
            SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
                SpatialPanelContent("Middle Right")
            }
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Bottom Right")
            }
        }
    }
}

@Composable
fun SpatialPanelContent(text: String) {
    Column(
        Modifier
            .background(color = Color.Black)
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Panel",
            color = Color.White,
            fontSize = 15.sp
        )
        Text(
            text = text,
            color = Color.White,
            fontSize = 25.sp,
            fontWeight = FontWeight.Bold
        )
    }
}

コードに関する主なポイント

  • SpatialRowSpatialColumnSpatialBoxSpatialLayoutSpacer はすべてサブスペース コンポーザブルであり、サブスペース内に配置する必要があります。
  • SubspaceModifier を使用してレイアウトをカスタマイズします。
  • 複数のパネルが 1 行に並ぶレイアウトの場合は、SubspaceModifier を使用してカーブの半径を 825dp に設定し、パネルがユーザーを囲むようにすることをおすすめします。詳しくは、設計に関するガイダンスをご覧ください。

ボリュームを使用してレイアウトに 3D オブジェクトを配置する

3D オブジェクトをレイアウトに配置するには、ボリュームと呼ばれるサブスペース コンポーザブルを使用する必要があります。方法の例を次に示します。

レイアウト内の 3D オブジェクトの例

Subspace {
    SpatialPanel(
        SubspaceModifier.height(1500.dp).width(1500.dp)
            .resizable().movable()
    ) {
        ObjectInAVolume(true)
            Box(
                Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "Welcome",
                    fontSize = 50.sp,
                )
            }
        }
    }
}

@Composable
fun ObjectInAVolume(show3DObject: Boolean) {
    val xrCoreSession = checkNotNull(LocalSession.current)
    val scope = rememberCoroutineScope()
    if (show3DObject) {
        Subspace {
            Volume(
                modifier = SubspaceModifier
                    .offset(volumeXOffset, volumeYOffset, volumeZOffset) //
Relative position
                    .scale(1.2f) // Scale to 120% of the size

            ) { parent ->
                scope.launch {
                   // Load your 3D Object here
                }
            }
        }
    }
}

コードに関する主なポイント

  • サブスペース修飾子に関する注意事項: SubspaceModifier API の順序に注意してください。
    • オフセットは修飾子チェーンで最初に指定する必要があります
    • 移動可能とサイズ変更可能は最後に記述する必要があります
    • 回転はスケールの前に適用する必要があります
  • ボリューム内に 3D コンテンツを読み込む方法について詳しくは、3D コンテンツの追加をご覧ください。

その他の空間 UI コンポーネントを追加する

空間 UI コンポーネントは、アプリの UI 階層内の任意の場所に配置できます。これらの要素は 2D UI で再利用できます。空間属性は、空間機能が有効になっている場合にのみ表示されます。これにより、コードを 2 回記述しなくても、メニュー、ダイアログ、その他のコンポーネントにエレベーションを追加できます。これらの要素の使用方法を詳しく理解するには、空間 UI の次の例をご覧ください。

UI コンポーネント

空間化が有効になっている場合

2D 環境の場合

SpatialDialog

パネルが Z 深度で少し後退し、エレベートされたダイアログが表示される

2D の Dialog にフォールバックします。

SpatialPopUp

パネルが Z ディープネスで少し後退し、エレベートされたポップアップが表示される

2D の PopUp にフォールバックします。

SpatialElevation

SpatialElevationLevel を設定すると、標高を追加できます。

空間的な標高のない番組。

SpatialDialog

以下は、少し遅れて開くダイアログの例です。SpatialDialog を使用すると、ダイアログは空間パネルと同じ Z 深度で表示され、空間化が有効になっている場合はパネルが 125 dp 後方に移動します。空間化が有効になっていない場合でも SpatialDialog は使用できます。この場合、2D の対応する Dialog にフォールバックします。

@Composable
fun DelayedDialog() {
   var showDialog by remember { mutableStateOf(false) }
   LaunchedEffect(Unit) {
       Handler(Looper.getMainLooper()).postDelayed({
           showDialog = true
       }, 3000)
   }
   if (showDialog) {
       SpatialDialog (
           onDismissRequest = { showDialog = false },
           SpatialDialogProperties(
               dismissOnBackPress = true)
       ){
           Box(Modifier
               .height(150.dp)
               .width(150.dp)
           ) {
               Button(onClick = { showDialog = false }) {
                   Text("OK")
               }
           }
       }
   }
}

コードに関する主なポイント

カスタム パネルとレイアウトを作成する

Compose for XR でサポートされていないカスタム パネルを作成するには、SceneCore API を使用して PanelEntities とシーングラフを直接操作します。

関連ドキュメント