Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Jetpack Compose
  3. Les listes
Extrait - Jetpack Compose Développez des interfaces accessibles et modernes pour Android
Extraits du livre
Jetpack Compose Développez des interfaces accessibles et modernes pour Android Revenir à la page d'achat du livre

Les listes

Introduction

Au sein d’une application, il est fréquent de devoir afficher une liste contenant beaucoup d’éléments. Dans ces cas-là, utiliser un layout basique comme une Row ou une Column peut engendrer de mauvaises performances. En effet, ces layouts vont composer, mesurer et dessiner tous les éléments de la liste en même temps, même ceux qui ne sont pas encore visibles à l’écran. C’est pour gérer de manière optimisée ce genre de situation que Compose UI fournit, à l’heure où ce livre est écrit, six fonctions Composables : LazyColumn, LazyRowLazyVerticalGrid, LazyHorizontalGrid, ainsi que LazyVerticalStaggeredGrid et LazyHorizontalStaggeredGrid. Ces fonctions permettent de réaliser des listes ou des grilles, comme illustré dans la figure suivante. Le rendu des éléments contenus dans ces six fonctions est optimisé puisque seuls les éléments qui sont visibles à l’écran sont composés, mesurés et dessinés.

images/08EI01.png

Lazy layouts

Dans la littérature, le terme « lazy layouts » est utilisé pour désigner ces six fonctions Composables.

Avant de découvrir en profondeur le fonctionnement de ces six layouts, illustrons à quel point avec Compose UI, implémenter une telle liste optimisée demande très peu de ligne de code. Dans cet exemple, nous utilisons LazyColumn pour représenter la liste des conversations au sein de notre application de messagerie :

val conversations by homeViewModel.conversations.collectAsState() 
LazyColumn(modifier = Modifier.fillMaxWidth()) { 
    items(conversations) { conversation -> 
        ConversationItem(conversation) ...

Définition du contenu via un DSL

Par rapport aux layouts standard que nous avons étudiés dans le chapitre Mise en page standard, les lazy layouts se différencient en mettant à disposition un Domain Specific Language ou DSL permettant de faciliter la déclaration de leur contenu. Comme dans l’exemple en introduction avec l’usage de la fonction items{} à l’intérieur du composant LazyColumn.

DSL : définition

Ouvrons une parenthèse pour définir ce qu’est un DSL. Elle désigne un langage qui est spécifique pour une partie d’une application. Un DSL est conçu pour rendre le code plus lisible et plus compréhensible, notamment en masquant l’implémentation interne et en supprimant le code redondant. En tant que développeurs et développeuses, nous utilisons quotidiennement un DSL dans nos projets Android, comme le code que nous écrivons dans nos scripts gradle. Nous utilisons dans ce cas-là une DSL écrite en langage Groovy ou Kotlin. Si nous reprenons par exemple l’extrait suivant du fichier build.gradle d’une application :

android { 
    compileSdk 33 
    defaultConfig { 
        applicationId "com.compose.livre" 
        minSdk 21 
        targetSdk 33 
        versionCode 1 
        versionName "1.0" 
        testInstrumentationRunner   
            "androidx.test.runner.AndroidJUnitRunner" 
        } ...

LazyColumn et LazyRow

Introduction

Après avoir découvert les différentes fonctions du DSL permettant de peupler une liste, attardons-nous maintenant dans cette section sur le fonctionnement global des layouts LazyColumn et LazyRow.

Paramètres

Analysons les différents paramètres des fonctions LazyRow et LazyColumn afin de voir comment personnaliser une liste. Pour cela, regardons les signatures de ces deux fonctions :

@Composable 
fun LazyRow( 
    modifier: Modifier = Modifier, 
    state: LazyListState = rememberLazyListState(), 
    contentPadding: PaddingValues = PaddingValues(0.dp), 
    reverseLayout: Boolean = false, 
    horizontalArrangement: Arrangement.Horizontal = 
        if (!reverseLayout)  
            Arrangement.Start  
        else  
            Arrangement.End, 
    verticalAlignment: Alignment.Vertical = Alignment.Top, 
    flingBehavior: FlingBehavior =   
        ScrollableDefaults.flingBehavior(), 
    userScrollEnabled: Boolean = true, 
    content: LazyListScope.() -> Unit 
) { 
    /*...*/ 
} 

@Composable 
fun LazyColumn( 
    modifier: Modifier = Modifier, 
    state: LazyListState = rememberLazyListState(), 
    contentPadding: PaddingValues = PaddingValues(0.dp), 
    reverseLayout: Boolean = false, 
    verticalArrangement: Arrangement.Vertical...

Grilles

Paramètres de LazyVerticalGrid et LazyHorizontalGrid

Les layouts LazyVerticalGrid et LazyHorizontalGrid permettent d’afficher plusieurs éléments sous forme de grille respectivement verticale ou horizontale. Analysons les différents paramètres de leurs fonctions Composables, afin de voir comment concevoir ce type de grille. Pour cela, voici les signatures de ces fonctions :

@Composable 
fun LazyVerticalGrid( 
    columns: GridCells, 
    modifier: Modifier = Modifier, 
    state: LazyGridState = rememberLazyGridState(), 
    contentPadding: PaddingValues = PaddingValues(0.dp), 
    reverseLayout: Boolean = false, 
    verticalArrangement: Arrangement.Vertical = 
        if (!reverseLayout)  
            Arrangement.Top  
        else 
            Arrangement.Bottom, 
    horizontalArrangement: Arrangement.Horizontal = 
        Arrangement.Start, 
    flingBehavior: FlingBehavior = 
        ScrollableDefaults.flingBehavior(), 
    userScrollEnabled: Boolean = true, 
    content: LazyGridScope.() -> Unit 
) { 
    /*...*/ 
} 

@Composable 
fun LazyHorizontalGrid( 
    rows: GridCells, 
    modifier: Modifier = Modifier, 
    state: LazyGridState = rememberLazyGridState(), 
    contentPadding: PaddingValues...

Contrôler et réagir à l’état de défilement

Le State Holder d’un lazy layout

Il est fréquent au sein d’une application de devoir réagir à l’état de défilement d’une liste ou de devoir actionner le défilement d’une liste vers une position donnée. Pour pouvoir effectuer cela, les lazy layouts disposent d’un State Holder. Il a la particularité d’être instancié par défaut dans les paramètres de ces layouts, comme nous pouvons le voir dans l’extrait de signature du lazy layout LazyColumn ci-après.

fun LazyColumn( 
    modifier: Modifier = Modifier,  
    state: LazyListState = rememberLazyListState(), 
    /*...*/ 
) { 
    /*...*/ 
} 

LazyListState correspond ici au State Holder stockant et gérant les états de ce layout. Il s’instancie via la fonction rememberLazyListState() qui se charge en même temps de sauvegarder cet état à travers les recompositions et changements de configuration, à la manière de la fonction rememberSaveable{}. Pour utiliser ce State Holder dans notre code, il suffit donc de l’instancier nous-même et de le fournir en paramètre du lazy layout, comme illustré ici :

val listState = rememberLazyListState() 
LazyColumn( 
    modifier = modifier, 
    state : LazyListState = listState 
) { 
    /*...*/ 
} 

Analysons maintenant plus en détail ce que LazyListState nous permet de faire.

Ce State Holder donne accès à différents états internes du layout, en particulier aux états suivants :

  • firstVisibleItemIndex, qui correspond...