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 Hauptaktivität auf die gleiche Weise aufgerufen werden wie setContent(). Ein Unterraum auf App-Ebene ist in Höhe, Breite und Tiefe unbegrenzt und bietet im Grunde eine unendliche Leinwand für räumliche Inhalte.
  • Subspace: Diese Komponente 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 Unterbereich 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.

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 Raumerkennung 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

  • Da SpatialPanel APIs Subspace-Kompositionen sind, müssen sie in Subspace oder setSubspaceContent aufgerufen werden. Wenn Sie sie außerhalb eines Unterraums aufrufen, wird eine Ausnahme ausgelöst.
  • Sie können dem Nutzer erlauben, die Größe des Steuerfelds zu ändern oder es zu verschieben, indem Sie die Modifikatoren movable oder resizable 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. Sie sind für die Verknüpfung mit einem entsprechenden Bereich, Layout oder einer anderen Entität vorgesehen. Ein Orbiter enthält in der Regel Navigations- und kontextbezogene Aktionselemente, die sich auf das angedockte Element 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

Wie im folgenden Beispiel gezeigt, rufen Sie einen Orbiter innerhalb des 2D-Layouts in einer SpatialPanel auf, um Nutzersteuerelemente wie die Navigation einzubinden. Dadurch 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

  • 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. Der Orbiter selbst wird ignoriert.
  • Weitere Informationen zur Verwendung und Gestaltung von Orbitaren 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 einem Kartenlayout 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 das 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 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

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 räumliche Darstellung 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 greift SpatialDialog auf sein 2D-Äquivalent Dialog zurück.

@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.

Orbiter an räumliche Layouts und andere Entitäten anpinnen

Sie können einen Orbiter an eine beliebige in Compose deklarierte Entität anpinnen. Dazu müssen Sie einen Orbiter in einem räumlichen Layout von UI-Elementen wie SpatialRow, SpatialColumn oder SpatialBox deklarieren. Der Orbiter wird an der übergeordneten Entität verankert, die sich am nächsten an der Stelle befindet, an der Sie ihn deklariert haben.

Das Verhalten des Orbiters wird dadurch bestimmt, wo Sie ihn deklarieren:

  • In einem 2D-Layout, das in einem SpatialPanel eingeschlossen ist (wie im vorangehenden Code-Snippet gezeigt), wird der Orbiter an diesem SpatialPanel verankert.
  • In einer Subspace wird der Orbiter an der nächstgelegenen übergeordneten Entität verankert, also dem räumlichen Layout, in dem der Orbiter deklariert ist.

Im folgenden Beispiel wird gezeigt, wie ein Orbiter an einer geografischen 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.titleLarge,
                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, wird er an der nächstgelegenen übergeordneten Entität verankert. In diesem Fall wird der Orbiter oben im SpatialRow verankert, in dem er deklariert ist.
  • Räumliche Layouts wie SpatialRow, SpatialColumn und SpatialBox sind alle mit entitätslosen Elementen verknüpft. Ein in einem räumlichen Layout deklarierter Orbiter wird daher an dieses Layout angedockt.

Siehe auch