Loupe

Android : afficher une liste d'éléments horizontalement avec un RecyclerView

Une fois que l'on a réussi à afficher un RecylerView, la question qui arrive souvent ensuite est : comment j'affiche les éléments de manière horizontale ? Il s'agit en effet d'une tendance UI apparue ces derniers temps dans les applications mobiles : afficher une liste de sous-listes dans lesquels on peut scroller horizontalement. Cela est par exemple le cas dans Netflix ou Spotify pour afficher les vidéos / musiques du moment par "catégorie". Dans cet article nous verrons donc comment créer des RecyclerView horizontaux - dans ou en dehors de RecyclerViews verticaux.

Pour rappel, cet article fait partie d'une série sur le RecyclerView :

  1. Android : Optimiser l'affichage de vos listes en utilisant un RecyclerView
  2. Android : ajouter des sticky headers sur vos RecyclerView
  3. Ajouter des en-têtes repliables (collapsible headers) sur vos RecyclerView
  4. Cet article
  5. Suspens

Voici un exemple en vidéo de ce que l'on souhaite faire dans une application de gestion de séries :

RecyclerView horizontal

C'est ici la partie la plus simple car il suffit de configurer le RecylerView en lui spécifiant un LayoutManager correctement instancié.

var layoutManager = new LinearLayoutManager(
     Application.Context,
     LinearLayoutManager.Horizontal,
     reverseLayout: false);

recyclerView.SetLayoutManager(layoutManager);

Le LayoutManager va indiquer au RecyclerView comment afficher ses éléments et dans notre cas on demande tout simplement un affichage horizontal.

Intégration dans un RecyclerView vertical 

La partie la plus intéressante ici est de l'intégrer au sein d'un autre RecyclerView qui va pour sa part afficher les éléments sur l'axe vertical. Après votre re-lecture de l'article initial sur la mise en place du RecyclerView, vous savez qu'il ne reste que cela à faire : 

  1. Créer une vue de cellule qui contient un RecyclerView.
  2. Ajouter le code nécessaire au ViewHolder pour configurer le RecyclerView de la cellule pour s'afficher de manière horizontale.

La définition de la vue correspondant à la cellule est vraiment ce qu'il y a de plus simple. J'en profite pour ajouter un loader que nous utiliserons plus tard.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.RecyclerView        
        android:id="@+id/HorizontaRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="170dp"
        android:scrollbars="horizontal" />

    <LoadingControl
        android:layout_weight="1"
        android:layout_gravity="center"
        android:id="@+id/Global_ListItem_Loader"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:indeterminate="true" />

</FrameLayout>

Comme j'utilise souvent ce genre de mécanisme, j'ai créé un ViewHolder générique qui s'occupe de faire l'initialisation correcte de ce genre de cellule. On retrouve le loader et le RecylerView dans la vue et on assigne le bon LayoutManager.

public class HorizontalRecyclerViewHolder : RecyclerView.ViewHolder
{
    public RecyclerView RecyclerView { get; }
    public View Loader { get; }

    public HorizontalRecyclerViewHolder(View itemView) : base(itemView)
    {
        RecyclerView = itemView.FindViewById<RecyclerView>(
                            Resource.Id.HorizontaRecyclerView);
        var layoutManager = new LinearLayoutManager(
             Application.Context,
             LinearLayoutManager.Horizontal,
             reverseLayout: false);
        RecyclerView.SetLayoutManager(layoutManager);
        Loader = itemView.FindViewById<View>(Resource.Id.Loader);
    }
} 

Afficher les éléments

Dans ce genre de scénario, je trouve cela important de ne charger les données à afficher que lorsque cela est nécessaire. Cela signifie uniquement lorsque le RecyclerView vertical parent demande de binder le ViewHolder correspondant à notre cellule "RecyclerView horizontal". 

Nous le ferons donc dans la méthode Fill de notre ViewHolder en suivant le pattern défini dans le premier article de la série. Je demande en paramètre de cette méthode une fonction capable de produire un Adapter que l'on va assigner sur notre RecyclerView.

internal async Task Fill(
            Func<ICanLoad,RecyclerView.Adapter> adapterCreator, 
            ICanLoad grp)
{
    Item = grp;
    ComputeLoaderVisibility(grp);
    
    if (!grp.IsLoading)
    {
        await  grp.LoadAsync();
    }

    ComputeLoaderVisibility(grp);

    var adapter = adapterCreator(grp);
    _currentAdapter = adapter;
    RecyclerView.SetAdapter(adapter);
}

private void ComputeLoaderVisibility(ShowSuggestionGroup grp)
{
    Loader.Visibility = 
               grp.IsLoading ? 
                       ViewStates.Visible : ViewStates.Gone;
}

Et... c'est tout ce que cela nécessite !

Happy coding :)

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus