Jetpack Compose facilite l'utilisation d'éléments graphiques personnalisés. De nombreuses applications doivent pouvoir contrôler avec précision ce qui apparaît à l'écran, que ce soit simplement pour aligner un cadre ou un cercle, ou pour minutieusement agencer toute une série d'éléments graphiques aux styles variés. Grâce à l'approche déclarative de Compose, la configuration de tous les éléments graphiques est centralisée, plutôt que dispersée entre un appel de méthode et un objet Helper Paint
. Compose crée et met à jour les objets nécessaires de manière efficace.
Éléments graphiques déclaratifs avec Compose
Compose étend son approche déclarative à la gestion des éléments graphiques. L'approche de Compose offre de nombreux avantages :
- Compose minimise l'état dans ses éléments graphiques, ce qui vous permet d'éviter les pièges liés à la programmation des états.
- Lorsque vous affichez un élément, toutes les options se trouvent là où elles sont attendues, dans la fonction modulable.
- Les API graphiques de Compose se chargent efficacement de créer et de libérer des objets.
Canvas
Le composable principal pour les éléments graphiques personnalisés est Canvas
.
Vous positionnez votre Canvas
dans votre mise en page de la même manière que n'importe quel autre élément d'interface utilisateur avec Compose. Dans le Canvas
, vous pouvez disposer des éléments avec un contrôle précis sur leur style et leur emplacement.
Par exemple, ce code crée un composable Canvas
qui remplit tout l'espace disponible dans son élément parent :
Canvas(modifier = Modifier.fillMaxSize()) {
}
Canvas
expose automatiquement un environnement d'affichage cloisonné DrawScope
, qui maintient son propre état. Cela vous permet de définir les paramètres d'un groupe d'éléments graphiques. DrawScope
fournit plusieurs champs utiles, tels que size
, un objet Size
spécifiant les dimensions actuelles et maximales de l'environnement DrawScope
.
À titre d'exemple, supposons que vous souhaitiez dessiner une ligne diagonale reliant l'angle supérieur droit à l'angle inférieur gauche de votre Canvas. Pour ce faire, vous devez ajouter un composable drawLine
:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
Figure 1 : Utilisation de drawLine
pour tracer une ligne sur le Canvas. Le code définit la couleur de la ligne, mais utilise la largeur par défaut.
Vous pouvez utiliser d'autres paramètres pour personnaliser l'élément graphique. Par défaut, la ligne de cet exemple est tracée avec une largeur minimale (un pixel), quelle que soit l'échelle de l'élément. Vous pouvez remplacer la valeur par défaut en définissant une valeur strokeWidth
:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue, strokeWidth = 5F ) }
Figure 2 : Remplacement de la largeur par défaut pour modifier la ligne de la figure 1.
Il existe de nombreuses autres fonctions de dessin simples, telles que drawRect
et drawCircle
.
Par exemple, ce code affiche un cercle plein et centré avec un diamètre égal à la moitié de la dimension la plus courte de l'environnement :
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
drawCircle(
color = Color.Blue,
center = Offset(x = canvasWidth / 2, y = canvasHeight / 2),
radius = size.minDimension / 4
)
}
Figure 3 : Utilisation de drawCircle
pour afficher un cercle au centre de l'environnement.
Par défaut, drawCircle
génère un cercle plein. Il n'est pas nécessaire de spécifier ce paramètre explicitement.
Les fonctions de dessin disposent de paramètres par défaut utiles. Par exemple, par défaut, drawRectangle()
remplit tout l'environnement du parent, et le rayon de drawCircle()
vaut la moitié de la dimension la plus courte du parent. Comme toujours en Kotlin, vous pouvez rendre votre code beaucoup plus simple et plus clair en exploitant les valeurs de paramètre par défaut pour ne définir que les paramètres à modifier. Vous pouvez en profiter en fournissant des paramètres explicites pour les méthodes de dessin DrawScope
, car vos éléments graphiques baseront leurs paramètres par défaut sur les paramètres de l'environnement parent.
DrawScope
Comme indiqué, chaque Canvas
Compose expose un DrawScope
, un environnement d'affichage cloisonné dans lequel sont exécutées vos commandes de dessin.
Par exemple, le code suivant affiche un rectangle en haut à gauche de l'environnement :
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasQuadrantSize = size / 2F
drawRect(
color = Color.Green,
size = canvasQuadrantSize
)
}
Vous pouvez utiliser la fonction DrawScope.inset()
pour ajuster les paramètres par défaut de la portée actuelle afin de modifier les limites de tracé et de translater les éléments graphiques en conséquence. Les opérations telles que inset()
s'appliquent à toutes les opérations de dessin dans le lambda correspondant :
val canvasQuadrantSize = size / 2F
inset(50F, 30F) {
drawRect(
color = Color.Green,
size = canvasQuadrantSize
)
}
DrawScope
propose d'autres transformations simples, telles que rotate()
.
Par exemple, ce code dessine un rectangle centré qui occupe un neuvième de l'environnement :
val canvasSize = size
val canvasWidth = size.width
val canvasHeight = size.height
drawRect(
color = Color.Gray,
topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
size = canvasSize / 3F
)
Figure 4 : Utilisation de drawRect
pour afficher un rectangle rempli au centre de l'écran.
Vous pouvez faire pivoter le rectangle en appliquant une rotation à sa DrawScope
:
rotate(degrees = 45F) {
drawRect(
color = Color.Gray,
topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
size = canvasSize / 3F
)
}
Figure 5 : L'opération rotate()
nous permet d'appliquer une rotation à l'environnement d'affichage actuel, ce qui fait pivoter le rectangle de 45 degrés.
Si vous souhaitez appliquer plusieurs transformations à vos éléments graphiques, la meilleure approche n'est pas de créer des environnements DrawScope
imbriqués, mais d'utiliser la fonction withTransform()
, qui génère et applique une seule transformation combinant toutes les modifications souhaitées. Il est plus efficace d'utiliser withTransform()
que de passer des appels imbriqués pour des transformations individuelles, car toutes les transformations sont effectuées ensemble en une seule opération, ce qui évite à Compose de devoir calculer et enregistrer chaque étape imbriquée.
Par exemple, ce code applique une translation et une rotation au rectangle :
withTransform({
translate(left = canvasWidth / 5F)
rotate(degrees = 45F)
}) {
drawRect(
color = Color.Gray,
topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
size = canvasSize / 3F
)
}
Figure 6 : Ici, nous utilisons withTransform
pour appliquer à la fois une rotation et une translation, pour faire pivoter le rectangle et le décaler vers la gauche.