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 bekannter Compose-Konzepte wie Zeilen und Spalten erstellen. So können Sie Ihre vorhandene Android-Benutzeroberfläche in den 3D-Raum erweitern oder völlig neue immersive 3D-Anwendungen erstellen.

Wenn Sie eine vorhandene Android Views-basierte App orten möchten, haben Sie mehrere Entwicklungsoptionen. Sie können Interoperabilitäts-APIs verwenden, Compose und Ansichten zusammen verwenden oder direkt mit der SceneCore-Bibliothek arbeiten. Weitere Informationen finden Sie in unserem Leitfaden zur Arbeit mit Ansichten.

Unterräume und lokalisierte Komponenten

Wenn Sie eine App für Android XR entwickeln, ist es wichtig, die Konzepte Unterraum und räumliche Komponenten zu verstehen.

Subspace

Wenn Sie für Android XR entwickeln, müssen Sie Ihrer App oder Ihrem Layout einen Unterraum hinzufügen. Ein Unterraum ist eine Partition des 3D-Raums in Ihrer App, in der Sie 3D-Inhalte platzieren, 3D-Layouts erstellen und ansonsten 2D-Inhalten Tiefe verleihen können. Ein Unterraum wird nur gerendert, wenn die Spatialisierung aktiviert ist. Im Home Space oder auf Nicht-XR-Geräten wird jeglicher Code in diesem Unterraum ignoriert.

Es gibt zwei Möglichkeiten, einen Unterraum zu erstellen:

  • setSubspaceContent: Mit dieser Funktion wird ein Unterraum auf App-Ebene erstellt. Diese Funktion kann in Ihrer MainActivity auf die gleiche Weise aufgerufen werden wie setContent. Ein Unterraum auf App-Ebene ist unbegrenzt in Höhe, Breite und Tiefe und bietet im Grunde ein unbegrenztes Canvas für räumliche Inhalte.
  • Subspace: Dieses Composeable kann überall in der UI-Hierarchie Ihrer App platziert werden. So können Sie Layouts für 2D- und räumliche UIs beibehalten, ohne den Kontext zwischen den Dateien zu verlieren. So können Sie z. B. die vorhandene App-Architektur zwischen XR und anderen Formfaktoren teilen, ohne den Status durch den gesamten UI-Baum hochladen oder Ihre App neu entwerfen zu müssen.

Weitere Informationen finden Sie unter Ihrer App einen untergeordneten Bereich hinzufügen.

Informationen zu lokalisierten Komponenten

Subspace-Kompositionen: Diese Komponenten können nur in einem Unterbereich gerendert werden. Sie müssen in Subspace oder setSubspaceContent eingeschlossen sein, bevor sie in ein 2D-Layout eingefügt werden können. Mit einem SubspaceModifier können Sie Ihren Subspace-Kompositionen Attribute wie Tiefe, Offset und Position hinzufügen.

  • Hinweis zu Unterraummodifikatoren: Achten Sie genau auf die Reihenfolge der SubspaceModifier APIs.
    • Der Offset muss zuerst in einer Modifikatorkette auftreten.
    • „Beweglich“ und „Größenanpassbar“ müssen als letztes angegeben werden.
    • Drehen muss vor dem Skalieren angewendet werden

Andere lokalisierte Komponenten müssen nicht innerhalb eines Unterraums aufgerufen werden. Sie bestehen aus herkömmlichen 2D-Elementen, die in einem räumlichen Container verpackt sind. Diese Elemente können in 2D- oder 3D-Layouts verwendet werden, wenn sie für beide definiert sind. Wenn die räumliche Darstellung nicht aktiviert ist, werden die räumlichen Elemente ignoriert und es wird auf die 2D-Entsprechungen zurückgegriffen.

Einen Bereich für die Raumaufzeichnung erstellen

Ein SpatialPanel ist ein zusammensetzbarer Unterraum, in dem Sie App-Inhalte anzeigen können. So können Sie beispielsweise Videowiedergabe, Standbilder oder andere Inhalte in einem Raumbereich anzeigen.

Beispiel für einen Bereich der visuellen Benutzeroberfläche

Mit SubspaceModifier können Sie die Größe, das Verhalten und die Position des Bereichs „Statistiken für die Standortermittlung“ ändern, wie im folgenden Beispiel gezeigt.

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

Wichtige Punkte zum Code

  • Hinweis zu Unterraummodifikatoren: Achten Sie genau auf die Reihenfolge der SubspaceModifier APIs.
    • Der Offset muss zuerst in einer Modifikatorkette auftreten.
    • Verschiebbare und skalierbare Modifikatoren müssen als letztes angegeben werden.
    • Die Drehung muss vor dem Skalieren angewendet werden.
  • Da SpatialPanel APIs Subspace-Kompositionen sind, müssen Sie sie in Subspace oder setSubspaceContent aufrufen. Wenn Sie sie außerhalb eines Unterraums aufrufen, wird eine Ausnahme ausgelöst.
  • Ermöglichen Sie es Nutzern, die Größe des Steuerfelds zu ändern oder es zu verschieben, indem Sie .movable oder .resizable SubspaceModifier hinzufügen.
  • Weitere Informationen zu Größe und Positionierung finden Sie in unserer Anleitung zum Design von Infofeldern. Weitere Informationen zur Codeimplementierung finden Sie in unserer Referenzdokumentation.

Orbiter erstellen

Ein Orbiter ist eine räumliche UI-Komponente. Es ist für die Anbringung an einem entsprechenden Infofeld für Orte vorgesehen und enthält Navigations- und Kontextaktionselemente, die sich auf dieses Infofeld beziehen. Wenn Sie beispielsweise ein räumliches Steuerfeld zum Anzeigen von Videoinhalten erstellt haben, können Sie die Steuerelemente für die Videowiedergabe in einem Orbiter hinzufügen.

Beispiel für einen Orbiter

Rufen Sie wie im folgenden Beispiel einen Orbiter in einem SpatialPanel auf, um Steuerelemente wie die Navigation einzubinden. Dabei werden sie aus Ihrem 2D-Layout extrahiert und gemäß Ihrer Konfiguration an das Infofeld für die Raumaufteilung angehängt.

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

Wichtige Punkte zum Code

  • Hinweis zu Subspace-Modifizierern: Achten Sie genau auf die Reihenfolge der SubspaceModifier APIs.
    • Der Offset muss zuerst in einer Modifikatorkette auftreten.
    • „Beweglich“ und „Größenanpassbar“ müssen als letztes angegeben werden.
    • Drehen muss vor dem Skalieren angewendet werden
  • Da Orbiter räumliche UI-Komponenten sind, kann der Code in 2D- oder 3D-Layouts wiederverwendet werden. Bei einem 2D-Layout werden in Ihrer App nur die Inhalte innerhalb des Orbiters gerendert und der Orbiter selbst wird ignoriert.
  • Weitere Informationen zur Verwendung und Gestaltung von Orbitern finden Sie in unseren Designrichtlinien.

Einem geografischen Layout mehrere Kartenbereiche hinzufügen

Mit SpatialRow, SpatialColumn, SpatialBox und SpatialLayoutSpacer können Sie mehrere Kartenkacheln erstellen und in SpatialLayout platzieren.

Beispiel für mehrere Kartenbereiche in einem Kartenlayout

Im folgenden Codebeispiel wird gezeigt, wie Sie dies umsetzen.

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

  • SpatialRow, SpatialColumn, SpatialBox und SpatialLayoutSpacer sind alle Unterraum-Kompositen und müssen in einem Unterraum platziert werden.
  • Mit SubspaceModifier können Sie das Layout anpassen.
  • Für Layouts mit mehreren Bereichen in einer Zeile empfehlen wir, mithilfe eines SubspaceModifier einen Kurvenradius von 825 dp festzulegen, damit die Bereiche den Nutzer umgeben. Weitere Informationen finden Sie in unseren Designrichtlinien.

Mit einem Volume ein 3D‑Objekt in Ihr Layout einfügen

Wenn Sie ein 3D‑Objekt in Ihr Layout einfügen möchten, müssen Sie ein zusammengesetztes Unterraumelement namens Volumen verwenden. 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) {
    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
                }
            }
        }
    }
}

Wichtige Punkte zum Code

  • Hinweis zu Subspace-Modifizierern: Achten Sie genau auf die Reihenfolge der SubspaceModifier APIs.
    • Der Offset muss zuerst in einer Modifikatorkette auftreten.
    • „Beweglich“ und „Größenanpassbar“ müssen als letztes angegeben werden.
    • Drehen muss vor dem Skalieren angewendet werden
  • Weitere Informationen zum Laden von 3D-Inhalten in einem Volume finden Sie unter 3D-Inhalte hinzufügen.

Andere UI-Komponenten für die Darstellung von Räumen hinzufügen

Räumliche UI-Komponenten können überall in der UI-Hierarchie Ihrer Anwendung platziert werden. Diese Elemente können in Ihrer 2D-Benutzeroberfläche wiederverwendet werden. Ihre räumlichen Attribute sind nur sichtbar, wenn die räumlichen Funktionen aktiviert sind. So können Sie Menüs, Dialogfelder und andere Komponenten hervorheben, ohne den Code zweimal schreiben zu müssen. Anhand der folgenden Beispiele für die räumliche Benutzeroberfläche können Sie besser nachvollziehen, wie Sie diese Elemente verwenden.

UI-Komponente

Wenn die Spatialisierung aktiviert ist

In einer 2D-Umgebung

SpatialDialog

Der Bereich wird in der Z‑Tiefe leicht nach hinten verschoben, um ein erhöhtes Dialogfeld anzuzeigen.

Kehrt zur 2D-Ansicht Dialog zurück.

SpatialPopUp

Der Bereich wird in der Z‑Tiefe leicht nach hinten verschoben, um ein erhöhtes Pop-up anzuzeigen.

Es wird zu einer 2D-PopUp zurückgekehrt.

SpatialElevation

Mit SpatialElevationLevel können Sie Höhen hinzufügen.

Darstellungen ohne räumliche Erhebungen.

SpatialDialog

Das ist ein Beispiel für ein Dialogfeld, das nach einer kurzen Verzögerung geöffnet wird. Wenn SpatialDialog verwendet wird, wird das Dialogfeld in derselben Z‑Tiefe wie das räumliche Steuerfeld angezeigt. Wenn die Spatialisierung aktiviert ist, wird das Steuerfeld um 125 dp nach hinten verschoben. SpatialDialog kann auch verwendet werden, wenn die Spatialisierung nicht aktiviert ist. In diesem Fall wird das 2D-Äquivalent Dialog verwendet.

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

Wichtige Punkte zum Code

Benutzerdefinierte Bereiche und Layouts erstellen

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

Siehe auch