1. Avant de commencer
Points abordés
- Expériences utilisateur uniques rendues possibles par Android XR.
- Optimiser une application pour un casque XR Android à l'aide de la bibliothèque Jetpack Compose XR
- Utiliser des éléments d'interface utilisateur de la bibliothèque Jetpack Compose XR
- Ressources permettant d'en savoir plus sur la création d'applications pour Android XR.
Ce que cet atelier n'est pas
- Un guide de création d'applications Android XR sans l'utilisation de Compose. Consultez la page Développer une UI pour des applications Android basées sur des vues.
- Un guide de création d'applications Unity ou OpenXR pour Android XR. Consultez les pages Développer avec Unity pour Android XR et Développer avec OpenXR.
Ce dont vous avez besoin
- La dernière version preview d'Android Studio.
- Ordinateur capable d'exécuter l'émulateur Android XR.
- Expérience avec Kotlin et Jetpack Compose, par exemple en ayant terminé le cours Principes de base d'Android avec Compose.
- Expérience dans la création d'appareils Android virtuels et dans l'exécution d'applications sur ces derniers.
- Familiarisez-vous avec les modes et les panneaux spatiaux d'Android XR, par exemple en suivant l'atelier de programmation Principes de base d'Android XR : Partie 1 – Modes et panneaux spatiaux.
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez optimiser une application avec certaines fonctionnalités XR existantes en ajoutant des éléments d'interface utilisateur flottants et en personnalisant l'environnement virtuel qui entoure l'utilisateur lorsqu'il utilise l'application.
Point de départ | Résultat final |
2. Configuration
Obtenir le code
- Le code pour cet atelier de programmation se trouve dans le répertoire
xr-fundamentals
au sein du dépôt GitHubxr-codelabs
. Pour cloner le dépôt, exécutez la commande suivante :
git clone https://github.com/android/xr-codelabs.git
- Vous pouvez aussi télécharger le dépôt sous la forme d'un fichier ZIP :
Ouvrir le projet
- Après avoir démarré Android Studio, importez le répertoire
xr-fundamentals/part1
. Le répertoirexr-fundamentals/part2
contient le code de solution, que vous pouvez consulter à tout moment en cas de difficulté ou pour avoir un aperçu du projet dans son ensemble.
Vous familiariser avec le code
- Après avoir ouvert le projet dans Android Studio, prenez le temps d'examiner le code de démarrage.
- Si vous n'avez pas encore suivi le premier atelier de programmation ou utilisé l'émulateur Android XR, suivez la procédure décrite dans la section Exécuter l'application dans l'émulateur Android XR pour exécuter l'application.
3. Apprendre les concepts de la réalité étendue: orbiteurs
Les orbiteurs sont des éléments d'interface utilisateur flottants disponibles en mode Espace complet. Ils sont généralement utilisés pour contrôler le contenu des panneaux spatiaux ou d'autres entités auxquelles leur orbite est ancrée. L'utilisation d'orbiteurs pour les commandes de contenu permet de gagner de l'espace. Les utilisateurs peuvent ainsi accéder rapidement aux fonctionnalités contenues dans les orbiteurs, tandis que le contenu principal reste visible. Les orbiteurs vous permettent d'intégrer des composants d'interface utilisateur existants (comme des barres de navigation) ou d'en créer.
De plus, l'API Orbiter vous permet d'afficher le contenu d'un orbiteur là où il devrait normalement s'afficher en mode d'affichage restreint ou sur un appareil non XR, et de le diviser automatiquement en orbiteur lorsqu'il s'exécute en mode d'affichage complet.
En mode d'affichage restreint, ce rail de navigation s'affiche dans le panneau principal de l'application. | En mode d'affichage complet, le rail de navigation est divisé en un élément orbiteur associé au panneau principal. |
À ce stade, l'application contient un bouton dans la barre d'application supérieure permettant de basculer entre le mode d'affichage restreint et le mode d'affichage complet. Ce bouton est un exemple parfait de commande pouvant être contenue dans un élément orbiteur en mode d'affichage complet. En effet, le fait de placer la commande en orbite autour du panneau principal permet de la mettre en valeur tout en indiquant visuellement qu'elle réduira le contenu de l'application dans ce panneau lorsque l'utilisateur cliquera dessus.
État actuel | Ce que vous allez implémenter |
Pour en savoir plus sur les considérations de conception des orbiteurs, consultez la section UI spatiale.
4. Ajouter un orbiteur
Encapsuler le bouton d'activation du mode d'affichage
Pour transformer le bouton d'activation du mode d'affichage en orbiteur, encapsulez le composable ToggleSpaceModeButton
dans un composable Orbiter
.
ui/component/XRFundamentalsTopAppBar .kt
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
...
Orbiter(
position = OrbiterEdge.Top,
alignment = Alignment.End,
offset = EdgeOffset.inner(16.dp),
shape = SpatialRoundedCornerShape(
CornerSize(percent = 100)
)
) {
ToggleSpaceModeButton()
}
Exécutez maintenant l'application : en mode d'affichage restreint, vous remarquerez que rien n'a changé. Toutefois, lorsque vous cliquez sur l'interrupteur et que l'application passe en mode d'affichage complet, vous remarquerez que le bouton n'est plus situé dans la barre d'application supérieure, mais en haut à droite du panneau spatial principal.
L'orbiteur est ancré au panneau spatial principal, car il s'agit de l'entité spatiale parente la plus proche dans l'arborescence de l'interface utilisateur. La position exacte de l'orbiteur par rapport au panneau spatial principal est déterminée par les paramètres position
, alignment
et offset
. Essayez de modifier ces paramètres pour voir la gamme de comportements qu'ils acceptent.
Mode d'affichage restreint | Mode d'affichage complet |
5. Apprendre les concepts de la réalité étendue : environnements spatiaux
Utiliser un Orbiter
pour personnaliser la position des éléments d'interface utilisateur dans l'espace 3D est un excellent moyen d'améliorer l'expérience utilisateur sur les appareils de réalité étendue. Vous pouvez améliorer encore l'expérience en personnalisant l'environnement spatial dans lequel se trouvent les utilisateurs lorsqu'ils utilisent votre application.
Les environnements spatiaux peuvent intégrer des éléments de profondeur, de texture et de géométrie 3D pour créer une expérience visuelle immersive riche. Pour ce faire, utilisez une image skybox sphérique (au format EXR) pour fournir un arrière-plan panoramique éloigné et/ou un élément de géométrie (au format glTF) pour fournir des éléments de premier et de deuxième plans pouvant se fondre dans une skybox. Par exemple, une application de streaming vidéo peut utiliser une skybox de nuit avec un glTF d'un cinéma drive-in avec un écran de projection et des voitures. Lorsque vous créez des éléments pour définir l'environnement spatial de vos utilisateurs, assurez-vous qu'ils offrent une résolution de haute qualité tout en conservant une taille de fichier raisonnable. Pour en savoir plus, consultez Optimiser les éléments d'environnement.
De plus, l'opacité de l'environnement spatial peut être contrôlée. Cela permet à un flux vidéo du monde réel de passer à travers l'environnement virtuel et de s'y fondre, ce qui peut aider les utilisateurs à se repérer.
À l'étape suivante, vous allez ajouter un élément géométrique à votre application et créer un menu pour permettre à l'utilisateur de choisir son environnement.
Pour en savoir plus sur la conception et l'implémentation des environnements spatiaux, consultez Environnements spatiaux et Ajouter des environnements spatiaux à votre application.
6. Autoriser les utilisateurs à modifier l'environnement spatial
Comment les applications contrôlent l'environnement spatial
Avant de commencer, il est important de comprendre comment les applications peuvent contrôler l'environnement spatial.
Contrairement au contenu des panneaux, les applications ne contrôlent pas directement l'environnement. Au lieu de cela, ils peuvent interagir avec la session SceneCore pour indiquer l'environnement qu'ils souhaitent que le système utilise. Cette préférence est représentée par un SpatialEnvironmentPreference
, qui se compose d'une image EXR de skybox et/ou d'un fichier gLTF de géométrie. Ce qui se passe lorsque votre application fournit une préférence dépend des capacités de votre application lorsqu'elle définit la préférence. Si votre application a la capacité de changer d'environnement, le système l'utilisera immédiatement. Si ce n'est pas le cas, la préférence sera appliquée lorsque votre application aura cette capacité.
Par exemple, les applications ne peuvent généralement pas changer d'environnement lorsqu'elles s'exécutent en mode d'affichage restreint, mais elles le peuvent généralement en mode d'affichage complet. Par conséquent, si vous autorisez un utilisateur à définir une préférence d'environnement en mode d'affichage restreint, cette préférence ne s'appliquera généralement que lorsque votre application s'exécutera en mode d'affichage complet.
Ajouter une dépendance à la bibliothèque XR SceneCore
Pour commencer à modifier l'environnement spatial, ajoutez une dépendance à la bibliothèque XR SceneCore, que vous utiliserez pour charger les éléments de l'environnement et définir les préférences de l'environnement. Vous devrez également ajouter une dépendance à l'artefact kotlinx-coroutines-guava
, car certaines API de chargement d'éléments utilisent le type de données ListenableFuture
.
libs.version.toml
[versions]
...
xrSceneCore = "1.0.0-alpha04"
kotlinxCoroutinesGuava = "1.10.2"
[libraries]
...
androidx-xr-scenecore = { group = "androidx.xr.scenecore", name = "scenecore", version.ref = "xrSceneCore"}
jetbrains-kotlinx-coroutines-guava = {group = "org.jetbrains.kotlinx", name="kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava"}
app/build.gradle.kts
dependencies {
...
implementation(libs.androidx.xr.scenecore)
implementation(libs.jetbrains.kotlinx.coroutines.guava)
...
}
Ajouter un élément d'environnement à votre projet
Pour spécifier une préférence d'environnement unique, vous aurez besoin d'une skybox et/ou d'un élément de géométrie. Pour cet atelier de programmation, vous n'utiliserez que l'élément géométrique green_hills_ktx2_mipmap.glb
, que vous trouverez dans le dossier part2
contenant le code de solution ou sur GitHub.
- Effectuez un clic droit sur le module d'application dans la fenêtre "Project" (Projet) d'Android Studio. Sélectionnez ensuite Nouveau > Dossier > Dossier d'éléments, puis cliquez sur Terminer pour créer le dossier.
- Ajoutez le fichier GLB dans le dossier
app/src/main/assets
que vous venez de créer.
Modéliser les options d'environnement
Pour simplifier l'interaction entre le code de l'interface utilisateur et les API système, vous pouvez créer une classe de données Kotlin pour modéliser chaque option d'environnement.
- Effectuez un clic droit sur le package
com.example.android.xrfundamentals
dans la fenêtre du projet, puis sélectionnez New > Package (Nouveau > Package). Saisissezcom.example.android.xrfundamentals.environment
comme nom de package. - Effectuez un clic droit sur le package , puis sélectionnez New > Kotlin Class/File (Nouveau > Classe/Fichier Kotlin). Saisissez
EnvironmentOption
comme nom, puis cliquez sur le type Classe de données. - Ajoutez le code suivant dans le fichier que vous venez de créer :
EnvironmentOption.kt
data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?)
val DEFAULT_ENVIRONMENT = EnvironmentOption("Default", null, null)
val ENVIRONMENT_OPTIONS = listOf(
DEFAULT_ENVIRONMENT,
EnvironmentOption("Green Hills", null, "green_hills_ktx2_mipmap.glb")
)
Ajout d'un assistant pour créer des éléments de chargement et renvoyer SpatialEnvironmentPreference
Vous pouvez ensuite ajouter une méthode d'assistance à la classe de données pour faciliter la conversion d'un EnvironmentOption
en SpatialEnvrionmentPreference
correspondant.
EnvironmentOption.kt
import androidx.xr.runtime.Session
import androidx.xr.scenecore.ExrImage
import androidx.xr.scenecore.GltfModel
import androidx.xr.scenecore.SpatialEnvironment
import kotlinx.coroutines.guava.await
...
data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?) {
suspend fun toSpatialEnvironmentPreference(session: Session): SpatialEnvironmentPreference? {
if (skyboxPath == null && geometryPath == null) {
return null
} else {
val skybox = skyboxPath?.let {
ExrImage.create(session, it).await()
}
val geometry = geometryPath?.let {
GltfModel.create(session, it).await()
}
return SpatialEnvironmentPreference(skybox, geometry)
}
}
}
Voici quelques points à noter :
- Si la skybox et la géométrie sont nulles, la valeur "null" est renvoyée pour indiquer que la préférence d'environnement système par défaut doit être utilisée. Pour en savoir plus, consultez
setSpatialEnvironmentPreference
. - Les ressources
skybox
etgeometry
sont créées de manière asynchrone, car ces éléments peuvent souvent être assez volumineux et prendre du temps à être lus en mémoire. Dans une application de production, vous pouvez envisager de mettre en cache ces éléments en mémoire si vous changez souvent d'environnement.
Implémenter l'interface utilisateur de sélection de l'environnement
Pour implémenter l'interface utilisateur, vous allez ajouter un deuxième orbiteur qui fait défiler les options d'environnement lorsque l'utilisateur clique dessus.
Ajouter l'orbiteur
- Effectuez un clic droit sur le module
app
dans la fenêtre du projet, puis sélectionnez New > Vector Asset (Nouveau > Élément vectoriel). Cliquez sur le champ Image clipart, puis recherchez et sélectionnez l'élémentlandscape
(dans la famille d'icônes Rempli). Cliquez ensuite sur OK, puis sur Suivant pour créer l'élément. - Effectuez un clic droit sur le package
com.example.android.xrfundamentals.ui.component
, puis sélectionnez New > Kotlin Class/File (Nouveau > Classe/Fichier Kotlin). SaisissezEnvironmentSelectionOrbiter
comme nom, puis cliquez sur le type File (Fichier). - Dans le fichier que vous venez de créer, ajoutez l'implémentation suivante du composable
EnvironmentSelectionOrbiter
.
EnvironmentSelectionOrbiter.kt
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
import com.example.android.xrfundamentals.R
@Composable
fun EnvironmentSelectionOrbiter(
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
Orbiter(
position = OrbiterEdge.Top,
alignment = Alignment.Start,
offset = EdgeOffset.inner(16.dp),
shape = SpatialRoundedCornerShape(
CornerSize(100)
)
) {
FilledTonalIconButton(
modifier = modifier,
onClick = onClick,
) {
Icon(painterResource(R.drawable.baseline_landscape_24), "Show environment selection dialog")
}
}
}
- Enfin, ajoutez
EnvironmentSelectionOrbiter
dans le panneau spatial principal.
XRFundamentalsApp.kt
import androidx.xr.compose.platform.LocalSpatialCapabilities
import com.example.android.xrfundamentals.ui.component.EnvironmentSelectionOrbiter
...
SpatialPanel(...) {
// Only show the environment selection orbiter if the app is actually able to
// change the environment
if (LocalSpatialCapabilities.current.isAppEnvironmentEnabled) {
EnvironmentSelectionOrbiter(
onClick = { TODO() }
)
}
...
}
Modifier l'environnement lorsque l'utilisateur clique sur l'orbiteur
Pour que tout fonctionne, il ne reste qu'une dernière étape : appeler setSpatialEnvironmentPreference
dans le gestionnaire de clics EnvironmentSelectionOrbiter
.
- Configurez une variable pour suivre l'option d'environnement actuelle (en dehors de
Subspace
afin que l'état soit conservé lorsque vous passez du mode d'affichage restreint au mode d'affichage complet). Créez également des variables pour la session XR actuelle et un champ d'application de coroutine pour appeler l'assistanttoSpatialEnvironmentPreference
.
XRFundamentalsApp.kt
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.xr.compose.platform.LocalSession
...
var currentEnvironmentOptionIndex by remember { mutableStateOf(0) }
Subspace {
val session = checkNotNull(LocalSession.current)
val scope = rememberCoroutineScope()
...
}
- Implémentez le rappel
onClick
pour faire défiler les options d'environnement.
XRFundamentalsApp.kt
EnvironmentSelectionOrbiter(
onClick = {
scope.launch {
currentEnvironmentOptionIndex =
(currentEnvironmentOptionIndex + 1) % ENVIRONMENT_OPTIONS.size
session.scene.spatialEnvironment.setSpatialEnvironmentPreference(
ENVIRONMENT_OPTIONS[currentEnvironmentOptionIndex].toSpatialEnvironmentPreference(
session
)
)
}
}
)
Exécutez à nouveau votre application et vérifiez que vous pouvez passer de l'environnement Green Hills à l'environnement par défaut.
7. Félicitations
Pour continuer à apprendre comment exploiter Android XR au mieux, consultez les ressources et exercices ci-dessous :
Complément d'informations
- Le guide Concevoir pour la XR couvre les principes de conception et les bonnes pratiques concernant la création d'applications pour Android XR.
- Le guide Développer avec le SDK Jetpack XR contient des conseils techniques sur les API et outils que vous pouvez utiliser pour créer votre expérience Android XR.
- La page Consignes relatives à la qualité des applications Android XR précise les critères de création d'une excellente expérience utilisateur.
- Explorez l'exemple d'application Hello Android XR.
Défis
- Recherchez ou créez des éléments d'environnement supplémentaires, puis ajoutez-les en tant qu'options.
- Modifiez le contrôleur d'environnement et l'interface utilisateur pour permettre à l'utilisateur de définir ses préférences de transfert à l'aide de l'API
setPassthroughOpacityPreference
. Notez que le contrôle du transfert est soumis à une capacité différente de celle utilisée pour modifier les éléments de l'environnement.