Benutzeroberfläche mit Jetpack Compose for XR entwickeln

Mit Jetpack Compose for XR können Sie Ihre räumliche Benutzeroberfläche und Ihr Layout deklarativ mithilfe vertrauter Compose-Konzepte wie Zeilen und Spalten erstellen. Auf diese Weise können Sie Ihre vorhandene Android-Benutzeroberfläche auf den 3D-Raum erweitern oder komplett neue immersive 3D-Anwendungen erstellen.

Wenn Sie eine vorhandene Android Views-basierte App verräumen, haben Sie mehrere Entwicklungsoptionen. Sie können Interoperabilitäts-APIs verwenden, „Compose“ und „Views“ zusammen verwenden oder direkt mit der SceneCore-Bibliothek arbeiten. Weitere Informationen finden Sie in unserem Leitfaden zum Arbeiten mit Ansichten.

Über untergeordnete Räume und räumliche Komponenten

Wenn Sie Ihre App für Android XR schreiben, ist es wichtig, die Konzepte von untergeordneten Räumen und räumlichen Komponenten zu kennen.

Untergeordneter Gruppenbereich

Bei der Entwicklung für Android XR müssen Sie Ihrer App oder Ihrem Layout einen untergeordneten Raum hinzufügen. Ein untergeordneter Raum ist eine Partition eines 3D-Bereichs in Ihrer App, in dem Sie 3D-Inhalte platzieren, 3D-Layouts erstellen und ansonsten 2D-Inhalten Tiefe verleihen können. Ein untergeordneter Raum wird nur gerendert, wenn die Verräumlichung aktiviert ist. Im Home Space oder auf Nicht-XR-Geräten wird jeglicher Code innerhalb dieses untergeordneten Raums ignoriert.

Es gibt zwei Möglichkeiten, einen untergeordneten Space zu erstellen:

  • setSubspaceContent(): Mit dieser Funktion wird ein untergeordneter Gruppenbereich auf App-Ebene erstellt. Dies kann in Ihrer Hauptaktivität auf die gleiche Weise wie setContent() aufgerufen werden. Höhe, Breite und Tiefe eines untergeordneten Raums auf App-Ebene sind unbegrenzt und bieten im Grunde eine unendliche Leinwand für raumbezogene Inhalte.
  • Subspace: Diese zusammensetzbare Funktion kann an beliebiger Stelle in der UI-Hierarchie Ihrer App platziert werden. So können Sie Layouts für 2D- und räumliche UI verwalten, ohne den Kontext zwischen den Dateien zu verlieren. Dies erleichtert die gemeinsame Nutzung der vorhandenen Anwendungsarchitektur zwischen XR und anderen Formfaktoren, ohne dass der Zustand über den gesamten UI-Baum gezogen oder die App neu gestaltet werden muss.

Weitere Informationen finden Sie unter Untergeordneten Gruppenbereich zur App hinzufügen.

Raumbezogene Komponenten

Zusammensetzbare Komponenten in Unterbereichen: Diese Komponenten können nur in einem untergeordneten Bereich gerendert werden. Sie müssen in Subspace oder setSubspaceContent eingeschlossen werden, bevor sie in ein 2D-Layout platziert werden können. Mit einem SubspaceModifier können Sie den zusammensetzbaren Funktionen des untergeordneten Bereichs Attribute wie Tiefe, Offset und Position hinzufügen.

Andere räumliche Komponenten müssen innerhalb eines untergeordneten Raums nicht aufgerufen werden. Sie bestehen aus herkömmlichen 2D-Elementen, die in einen räumlichen Container eingebettet sind. Diese Elemente können in 2D- oder 3D-Layouts verwendet werden, wenn sie für beide definiert sind. Wenn die Raumisierung nicht aktiviert ist, werden ihre räumlichen Funktionen ignoriert und auf ihre 2D-Entsprechungen zurückgesetzt.

Räumliches Steuerfeld erstellen

Ein SpatialPanel ist eine zusammensetzbare Funktion für einen untergeordneten Bereich, mit dem Sie App-Inhalte anzeigen können, z. B. die Videowiedergabe, Standbilder oder andere Inhalte in einem räumlichen Steuerfeld.

Beispiel für ein räumliches UI-Steuerfeld

Mit SubspaceModifier können Sie die Größe, das Verhalten und die Position des räumlichen Steuerfelds ändern, wie im folgenden Beispiel gezeigt.

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

@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
        )
    }
}

Wichtige Punkte zum Code

  • Da es sich bei SpatialPanel APIs um zusammensetzbare Komponenten für untergeordnete Bereiche handelt, müssen sie innerhalb von Subspace oder setSubspaceContent aufgerufen werden. Wird sie außerhalb eines untergeordneten Bereichs aufgerufen, wird eine Ausnahme ausgelöst.
  • Erlauben Sie dem Nutzer, die Größe des Bereichs zu ändern oder das Feld zu verschieben, indem Sie die Modifikatoren movable oder resizable hinzufügen.
  • Weitere Informationen zur Größe und Positionierung finden Sie in unserer Anleitung für die Gestaltung von raumbezogenen Bereichen. Ausführliche Informationen zur Codeimplementierung finden Sie in unserer Referenzdokumentation.

Orbiter erstellen

Ein Orbiter ist eine räumliche UI-Komponente. Es ist für die Verbindung mit einem entsprechenden räumlichen Feld, Layout oder einer anderen Entität vorgesehen. Ein Orbiter enthält in der Regel Navigations- und kontextbezogene Aktionselemente, die mit der Entität zusammenhängen, auf der er verankert ist. Wenn Sie beispielsweise einen räumlichen Bereich zur Anzeige von Videoinhalten erstellt haben, können Sie Steuerelemente für die Videowiedergabe innerhalb eines Orbiters hinzufügen.

Beispiel für einen Orbiter

Wie im folgenden Beispiel gezeigt, kannst du einen Orbiter innerhalb des 2D-Layouts in einem SpatialPanel aufrufen, um Nutzersteuerelemente wie die Navigation zu umschließen. Dadurch werden sie entsprechend Ihrer Konfiguration aus Ihrem 2D-Layout extrahiert und an das räumliche Steuerfeld angehängt.

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

@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
                )
            }
        }
    }
}

Wichtige Punkte zum Code

  • Da Orbiter räumliche UI-Komponenten sind, kann der Code in 2D- oder 3D-Layouts wiederverwendet werden. Bei einem 2D-Layout rendert Ihre App nur den Inhalt innerhalb des Orbiters und ignoriert den Orbiter selbst.
  • Weitere Informationen zur Verwendung und Entwicklung von Orbitern findest du in unserem Designleitfaden.

Einem räumlichen Layout mehrere räumliche Steuerfelder hinzufügen

Mit SpatialRow, SpatialColumn, SpatialBox und SpatialLayoutSpacer kannst du mehrere räumliche Steuerfelder erstellen und in einem räumlichen Layout platzieren.

Beispiel für mehrere räumliche Panels in einem räumlichen Layout

Das folgende Codebeispiel zeigt, wie dies funktioniert.

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
        )
    }
}

Wichtige Punkte zum Code

Mit einem Volumen ein 3D-Objekt in Ihrem Layout platzieren

Um ein 3D-Objekt in Ihrem Layout zu platzieren, benötigen Sie eine zusammensetzbare Funktion für untergeordnete Bereiche, sogenannte Volumes. Hier ist ein Beispiel dafür.

Beispiel für ein 3D-Objekt in einem Layout

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) {

Wichtige Punkte zum Code

Weitere Komponenten für die räumliche Benutzeroberfläche hinzufügen

Räumliche UI-Komponenten können an einer beliebigen Stelle in der UI-Hierarchie Ihrer Anwendung platziert werden. Diese Elemente können in der 2D-UI wiederverwendet werden und ihre räumlichen Attribute sind nur sichtbar, wenn die räumlichen Funktionen aktiviert sind. Auf diese Weise können Sie Menüs, Dialogfeldern und anderen Komponenten Höhen hinzufügen, ohne den Code zweimal schreiben zu müssen. Die folgenden Beispiele für räumliche UI zeigen, wie diese Elemente verwendet werden.

UI-Komponente

Wenn die Verräumlichung aktiviert ist

In 2D-Umgebung

SpatialDialog

Das Steuerfeld wird in Z-Tiefe leicht nach hinten geschoben, um ein erweitertes Dialogfeld anzuzeigen

2D-Modus Dialog wird verwendet.

SpatialPopup

Das Steuerfeld schiebt sich in Z-Tiefe leicht nach hinten, um ein erhöhtes Pop-up anzuzeigen.

Es wird ein 2D-Popup-Element verwendet.

SpatialElevation

SpatialElevationLevel kann festgelegt werden, um eine Höhe hinzuzufügen.

Shows ohne räumliche Elevation.

SpatialDialog

Dies ist ein Beispiel für ein Dialogfeld, das nach einer kurzen Verzögerung geöffnet wird. Wenn SpatialDialog verwendet wird, wird das Dialogfeld mit derselben Z-Tiefe wie das räumliche Steuerfeld angezeigt und das Feld wird bei aktivierter Verräumlichung um 125 dp verschoben. SpatialDialog kann auch verwendet werden, wenn die Raumisierung nicht aktiviert ist. In diesem Fall greift SpatialDialog auf sein 2D-Gegenstück Dialog zurück.

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

Wichtige Punkte zum Code

Benutzerdefinierte Steuerfelder und Layouts erstellen

Wenn Sie benutzerdefinierte Bereiche erstellen möchten, die von Compose for XR nicht unterstützt werden, können Sie direkt mit PanelEntities und dem Szenendiagramm mithilfe der SceneCore APIs arbeiten.

Orbiter an räumlichen Layouts und anderen Elementen verankern

Sie können einen Orbiter an jeder in „Compose“ deklarierten Entität verankern. Dazu muss ein Orbiter in einem räumlichen Layout von UI-Elementen wie SpatialRow, SpatialColumn oder SpatialBox deklariert werden. Der Orbiter verankert sich bei der übergeordneten Entität, die der von Ihnen deklarierten Stelle am nächsten ist.

Das Verhalten des Orbiters hängt davon ab, wo du es deklarierst:

  • In einem 2D-Layout, das in ein SpatialPanel eingebunden ist (wie in einem vorherigen Code-Snippet gezeigt), verankert sich der Orbiter an diesem SpatialPanel.
  • In einem Subspace verankert sich der Orbiter bei der nächstgelegenen übergeordneten Entität, d. h. dem räumlichen Layout, in dem der Orbiter deklariert ist.

Das folgende Beispiel zeigt, wie ein Orbiter an einer räumlichen Zeile verankert wird:

Subspace {
    SpatialRow {
        Orbiter(
            position = OrbiterEdge.Top,
            offset = EdgeOffset.inner(8.dp),
            shape = SpatialRoundedCornerShape(size = CornerSize(50))
        ) {
            Text(
                "Hello World!",
                style = MaterialTheme.typography.h2,
                modifier = Modifier
                    .background(Color.White)
                    .padding(16.dp)
            )
        }
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
        ) {
            Box(
                modifier = Modifier
                    .background(Color.Red)
            )
        }
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
        ) {
            Box(
                modifier = Modifier
                    .background(Color.Blue)
            )
        }
    }
}

Wichtige Punkte zum Code

  • Wenn Sie einen Orbiter außerhalb eines 2D-Layouts deklarieren, verankert er sich an der nächstgelegenen übergeordneten Entität. In diesem Fall verankert sich der Orbiter am oberen Rand des SpatialRow, in dem er deklariert ist.
  • Räumlichen Layouts wie SpatialRow, SpatialColumn und SpatialBox sind inhaltslose Entitäten zugeordnet. Daher verankert ein in einem räumlich Layout deklarierter Orbiter bei diesem Layout.

Siehe auch