Avec Jetpack Compose for XR, vous pouvez créer de manière déclarative votre UI spatiale et votre mise en page à l'aide de concepts Compose familiers tels que les lignes et les colonnes. Vous pouvez ainsi étendre votre UI Android existante dans un espace 3D ou créer des applications 3D immersives entièrement nouvelles.
Si vous spatialisez une application Android basée sur les vues, plusieurs options de développement s'offrent à vous. Vous pouvez utiliser des API d'interopérabilité, Compose et Views ensemble, ou travailler directement avec la bibliothèque SceneCore. Pour en savoir plus, consultez notre guide sur l'utilisation des vues.
À propos des sous-espaces et des composants spatialisés
Lorsque vous écrivez votre application pour Android XR, il est important de comprendre les concepts d'sous-espace et de composants spatialisés.
À propos du sous-espace
Lorsque vous développez pour Android XR, vous devez ajouter un sous-espace à votre application ou à votre mise en page. Un sous-espace est une partition d'un espace 3D au sein de votre application. Vous pouvez y placer du contenu 3D, créer des mises en page 3D et ajouter de la profondeur à des contenus qui seraient en 2D autrement. Un sous-espace n'est affiché que lorsque la spatialisation est activée. Dans l'espace Home ou sur les appareils non XR, tout code de ce sous-espace est ignoré.
Il existe deux façons de créer un sous-espace:
setSubspaceContent()
: cette fonction crée un sous-espace au niveau de l'application. Vous pouvez l'appeler dans votre activité principale de la même manière que vous utilisezsetContent()
. Un sous-espace au niveau de l'application est illimité en hauteur, largeur et profondeur, fournissant essentiellement un canevas infini pour le contenu spatial.Subspace
: ce composable peut être placé n'importe où dans la hiérarchie de l'interface utilisateur de votre application, ce qui vous permet de conserver les mises en page pour l'UI 2D et spatiale sans perdre de contexte entre les fichiers. Cela permet de partager plus facilement des éléments tels que l'architecture d'application existante entre XR et d'autres facteurs de forme, sans avoir à hisser l'état dans l'ensemble de l'arborescence de l'UI ni à réarchitecturer votre application.
Pour en savoir plus, consultez Ajouter un sous-espace à votre application.
À propos des composants spatialisés
Composables de sous-espace: ces composants ne peuvent être affichés que dans un sous-espace.
Ils doivent être inclus dans Subspace
ou setSubspaceContent
avant d'être placés dans une mise en page 2D. Un SubspaceModifier
vous permet d'ajouter des attributs tels que la profondeur, le décalage et le positionnement à vos composables d'espaces sous-jacents.
Les autres composants spatialisés n'ont pas besoin d'être appelés dans un sous-espace. Ils se composent d'éléments 2D conventionnels encapsulés dans un conteneur spatial. Ces éléments peuvent être utilisés dans des mises en page 2D ou 3D s'ils sont définis pour les deux. Lorsque la spatialisation n'est pas activée, leurs éléments spatialisés sont ignorés et ils reviennent à leurs homologues 2D.
Créer un panneau spatial
Un SpatialPanel
est un composable d'espace sous-jacent qui vous permet d'afficher le contenu de l'application. Par exemple, vous pouvez afficher la lecture vidéo, des images fixes ou tout autre contenu dans un panneau spatial.
Vous pouvez utiliser SubspaceModifier
pour modifier la taille, le comportement et le positionnement du panneau spatial, comme illustré dans l'exemple suivant.
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 ) } }
Points clés concernant le code
- Étant donné que les API
SpatialPanel
sont des composables de sous-espace, vous devez les appeler dansSubspace
ousetSubspaceContent
. Les appeler en dehors d'un sous-espace génère une exception. - Autorisez l'utilisateur à redimensionner ou à déplacer le panneau en ajoutant les modificateurs
movable
ouresizable
. - Pour en savoir plus sur le dimensionnement et le positionnement, consultez nos conseils de conception pour les panneaux spatiaux. Pour en savoir plus sur l'implémentation du code, consultez notre documentation de référence.
Fonctionnement d'un modificateur de sous-espace mobile
Lorsqu'un utilisateur éloigne un panneau de lui, un modificateur de sous-espace mobile le met à l'échelle de la même manière que le système redimensionne les panneaux dans l'espace d'accueil. Tous les contenus enfants héritent de ce comportement. Pour désactiver cette fonctionnalité, définissez le paramètre scaleWithDistance
sur false
.
Créer un orbiteur
Un orbiteur est un composant d'interface utilisateur spatial. Il est conçu pour être associé à un panneau spatial, une mise en page ou une autre entité correspondante. Un orbiteur contient généralement des éléments de navigation et d'action contextuels liés à l'entité à laquelle il est ancré. Par exemple, si vous avez créé un panneau spatial pour afficher du contenu vidéo, vous pouvez ajouter des commandes de lecture vidéo dans un orbiteur.
Comme illustré dans l'exemple suivant, appelez un orbiteur dans la mise en page 2D dans un SpatialPanel
pour encapsuler les commandes utilisateur telles que la navigation. Vous les extrayez ainsi de votre mise en page 2D et les associez au panneau spatial en fonction de votre configuration.
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 ) } } } }
Points clés concernant le code
- Étant donné que les orbiteurs sont des composants d'UI spatiaux, le code peut être réutilisé dans des mises en page 2D ou 3D. Dans une mise en page 2D, votre application n'affiche que le contenu dans l'orbiteur et ignore l'orbiteur lui-même.
- Consultez nos conseils de conception pour en savoir plus sur l'utilisation et la conception des orbiteurs.
Ajouter plusieurs panneaux spatiaux à une mise en page spatiale
Vous pouvez créer plusieurs panneaux spatiaux et les placer dans une mise en page spatiale à l'aide de SpatialRow
, SpatialColumn
, SpatialBox
et SpatialLayoutSpacer
.
L'exemple de code suivant montre comment procéder.
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 ) } }
Points clés concernant le code
SpatialRow
,SpatialColumn
,SpatialBox
etSpatialLayoutSpacer
sont tous des composables de sous-espace et doivent être placés dans un sous-espace.- Utilisez
SubspaceModifier
pour personnaliser votre mise en page. - Pour les mises en page comportant plusieurs panneaux sur une ligne, nous vous recommandons de définir un rayon de courbe de 825 dp à l'aide d'un
SubspaceModifier
afin que les panneaux entourent l'utilisateur. Pour en savoir plus, consultez nos conseils de conception.
Utiliser un volume pour placer un objet 3D dans votre mise en page
Pour placer un objet 3D dans votre mise en page, vous devez utiliser un composable d'espace sous-jacent appelé volume. Voici un exemple de la procédure à suivre.
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) {
Informations supplémentaires
- Consultez Ajouter des modèles 3D à votre application pour mieux comprendre comment charger du contenu 3D dans un volume.
Ajouter une surface pour le contenu image ou vidéo
Un SpatialExternalSurface
est un composable de sous-espace qui crée et gère le Surface
dans lequel votre application peut dessiner du contenu, comme une image ou une vidéo. SpatialExternalSurface
est compatible avec les contenus stéréoscopiques ou monoscopiques.
Cet exemple montre comment charger une vidéo stéréoscopique côte à côte à l'aide de Media3 Exoplayer et de SpatialExternalSurface
:
@Composable fun SpatialExternalSurfaceContent() { val context = LocalContext.current Subspace { SpatialExternalSurface( modifier = SubspaceModifier .width(1200.dp) // Default width is 400.dp if no width modifier is specified .height(676.dp), // Default height is 400.dp if no height modifier is specified // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending // upon which type of content you are rendering: monoscopic content, side-by-side stereo // content, or top-bottom stereo content stereoMode = StereoMode.SideBySide, ) { val exoPlayer = remember { ExoPlayer.Builder(context).build() } val videoUri = Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) // Represents a side-by-side stereo video, where each frame contains a pair of // video frames arranged side-by-side. The frame on the left represents the left // eye view, and the frame on the right represents the right eye view. .path("sbs_video.mp4") .build() val mediaItem = MediaItem.fromUri(videoUri) // onSurfaceCreated is invoked only one time, when the Surface is created onSurfaceCreated { surface -> exoPlayer.setVideoSurface(surface) exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.play() } // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its // associated Surface are destroyed onSurfaceDestroyed { exoPlayer.release() } } } }
Points clés concernant le code
- Définissez
StereoMode
surMono
,SideBySide
ouTopBottom
, en fonction du type de contenu que vous affichez :Mono
: le frame de l'image ou de la vidéo se compose d'une seule image identique affichée aux deux yeux.SideBySide
: le frame d'image ou de vidéo contient une paire d'images ou de frames vidéo disposées côte à côte, l'image ou le frame de gauche représentant la vue de l'œil gauche, et l'image ou le frame de droite représentant la vue de l'œil droit.TopBottom
: le cadre d'image ou de vidéo contient une paire d'images ou de cadres vidéo empilées verticalement, l'image ou le cadre du haut représentant la vue de l'œil gauche et l'image ou le cadre du bas représentant la vue de l'œil droit.
SpatialExternalSurface
n'est compatible qu'avec les surfaces rectangulaires.Surface
ne capture pas les événements d'entrée.- Il est impossible de synchroniser les modifications
StereoMode
avec le rendu de l'application ou le décodage vidéo. - Ce composable ne peut pas s'afficher devant d'autres panneaux. Par conséquent, vous ne devez pas utiliser de modificateurs mobiles s'il existe d'autres panneaux dans la mise en page.
Ajouter d'autres composants d'interface utilisateur spatiaux
Les composants d'interface utilisateur spatiale peuvent être placés n'importe où dans la hiérarchie de l'interface utilisateur de votre application. Ces éléments peuvent être réutilisés dans votre UI 2D, et leurs attributs spatiaux ne seront visibles que lorsque les fonctionnalités spatiales sont activées. Vous pouvez ainsi ajouter une élévation aux menus, aux boîtes de dialogue et à d'autres composants sans avoir à écrire votre code deux fois. Consultez les exemples d'UI spatiale suivants pour mieux comprendre comment utiliser ces éléments.
Composant d'interface utilisateur |
Lorsque la spatialisation est activée |
Dans un environnement 2D |
---|---|---|
|
Le panneau recule légèrement en profondeur pour afficher une boîte de dialogue surélevée. |
Revenir en mode 2D |
|
Le panneau recule légèrement en profondeur pour afficher un pop-up surélevé |
Retourne à une |
|
|
Émissions sans élévation spatiale. |
SpatialDialog
Voici un exemple de boîte de dialogue qui s'ouvre après un court délai. Lorsque SpatialDialog
est utilisé, la boîte de dialogue s'affiche à la même profondeur Z que le panneau spatial, et le panneau est repoussé de 125 dp lorsque la spatialisation est activée. SpatialDialog
peut également être utilisé lorsque la spatialisation n'est pas activée, auquel cas SpatialDialog
utilise son équivalent 2D, Dialog
.
@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") } } } } }
Points clés concernant le code
- Voici un exemple de
SpatialDialog
. L'utilisation deSpatialPopup
et deSpatialElevation
est très similaire. Pour en savoir plus, consultez notre documentation de référence de l'API.
Créer des panneaux et des mises en page personnalisés
Pour créer des panneaux personnalisés non compatibles avec Compose pour XR, vous pouvez travailler directement avec PanelEntities
et le graphique de scène à l'aide des API SceneCore
.
Ancrer des orbiteurs à des mises en page spatiales et à d'autres entités
Vous pouvez ancrer un orbiteur à n'importe quelle entité déclarée dans Compose. Pour ce faire, déclarez un orbiteur dans une mise en page spatiale d'éléments d'UI tels que SpatialRow
, SpatialColumn
ou SpatialBox
. L'orbiteur s'ancre à l'entité parente la plus proche de l'endroit où vous l'avez déclaré.
Le comportement de l'orbiteur est déterminé par l'endroit où vous le déclarez:
- Dans une mise en page 2D encapsulée dans un
SpatialPanel
(comme illustré dans un extrait de code précédent), l'orbiteur s'ancre à cet élémentSpatialPanel
. - Dans un
Subspace
, l'orbiteur s'ancre à l'entité parente la plus proche, qui correspond à la mise en page spatiale dans laquelle l'orbiteur est déclaré.
L'exemple suivant montre comment ancrer un orbiteur à une ligne spatiale:
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) ) } } }
Points clés concernant le code
- Lorsque vous déclarez un orbiteur en dehors d'une mise en page 2D, il s'ancre à son entité parente la plus proche. Dans ce cas, l'orbiteur s'ancre en haut de l'
SpatialRow
dans laquelle il est déclaré. - Les mises en page spatiales telles que
SpatialRow
,SpatialColumn
etSpatialBox
sont toutes associées à des entités sans contenu. Par conséquent, un orbiteur déclaré dans une mise en page spatiale s'ancre à cette mise en page.
Voir aussi
- Ajouter des modèles 3D à votre application
- Développer une UI pour des applications Android basées sur des vues
- Implémenter Material Design pour XR