Rendre le nettoyeur de code de l'adaptateur avec MergeAdapter

Fatigué des adaptateurs surchargés et complexes de votre projet comme l'image ci-dessous? Chaque fois que vous ajoutez un nouveau type de cellule, souhaitez-vous réécrire l'adaptateur pour RecyclerView afin de rendre le code plus facile à lire? Il existe de nombreuses approches, le plus souvent, il est recommandé d'utiliser l'approche de l'adaptateur de délégué ou, par exemple, une bibliothèque pour créer dynamiquement des listes avec différents types de vues comme une groupie, avec laquelle vous pouvez vous familiariser dans cet article . Mais aujourd'hui, nous allons parler d'une nouvelle classe qui aidera à encapsuler la logique de votre adaptateur pour différentes cellules, respectant ainsi les principes de SOLID.



image



MergeAdapter est une nouvelle classe introduite dans recyclerview: 1.2.0-alpha02 qui vous permet de combiner plusieurs cartes graphiques en un seul RecyclerView. Cela vous permettra d'encapsuler la logique de chaque cellule de votre adaptateur, et vous permettra de la réutiliser à l'avenir.



Problème



Commençons par un exemple. Supposons que nous ayons une tâche pour afficher un flux avec deux types de données - un texte avec une description et une image. Le code de la méthode onCreateViewHolder dans le cas le plus courant ressemblera à ceci:



override fun onCreateViewHolder(
    parent: ViewGroup, viewType: Int
): RecyclerView.ViewHolder? {
    val holder: RecyclerView.ViewHolder
    val inflater = LayoutInflater.from(parent.context)
    when (viewType) {
        TEXT_VIEW_TYPE -> {
            holder = TextViewHolder(
                inflater.inflate(R.layout.text_item, parent, false)
            )
        }
        IMAGE_VIEW_TYPE -> {
            holder = ImageViewHolder(
                inflater.inflate(R.layout.image_item, parent, false),
                imageClickListener
            )
        }
        else -> {
            throw IllegalArgumentException(
                "Can't create view holder from view type $viewType"
            )
        }
    }
    return holder
}


Pourquoi est-ce mauvais? L'inconvénient de cette mise en œuvre est en violation des principes de DRY et SOLID (responsabilité unique et ouvert fermé). Pour s'en assurer, il suffit d'ajouter deux conditions: entrez un nouveau type de données (case à cocher) et une autre bande, où il n'y aura que des cases à cocher et des images.



Nous sommes confrontés à un choix - utiliser le même adaptateur pour la deuxième bande ou en créer une nouvelle? Quelle que soit la solution que nous choisirons, nous devrons changer le code (à peu près la même chose, mais à des endroits différents). Il sera nécessaire d'ajouter une nouvelle VIEW_TYPE, ViewHolder méthodes nouvelles et modifier: getItemViewType(), onCreateViewHolder() onBindViewHolder().



Si nous décidons de conserver un seul adaptateur, les modifications s'arrêteront là. Mais si à l'avenir de nouveaux types de données avec une nouvelle logique sont ajoutés uniquement au deuxième flux, le premier aura des fonctionnalités supplémentaires, et il devra également être testé, bien qu'il n'ait pas changé.



Si nous décidons de créer un nouvel adaptateur, il y aura beaucoup de code en double.



Décision



La nouvelle classe MergeAdapter vous permet de combiner différents adaptateurs pour différents types de cellules. Par exemple, un cas d'utilisation très courant est d'afficher un spinner lors du chargement de données dans le flux, et si, soudainement, une erreur de chargement se produit, d'afficher une cellule avec une erreur à la fin du flux.



image



La solution à ce problème peut être l'utilisation de MergeAdapter Supposons que nous ayons 3 adaptateurs:




val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, thirdAdapter)
recyclerView.adapter = mergeAdapter


Le RecyclerView affichera les éléments de chaque adaptateur de manière séquentielle, dans le même ordre qu'ils ont été passés au constructeur. Différents adaptateurs vous permettent de séparer la logique des différentes cellules de la liste. Par exemple, si vous devez ajouter un titre à la liste, vous n'avez pas besoin d'implémenter cette logique dans l'adaptateur, qui est responsable de l'affichage du contenu principal dans la liste, vous pouvez séparer les adaptateurs pour différents types de cellules. Cette approche permet d'encapsuler la logique et de la réutiliser à l'avenir pour différents écrans.



image



Affichez le téléchargement dans l'en-tête ou en bas de la liste.



Pour afficher l'état du téléchargement en haut ou en bas de la liste, vous devez ajouter des adaptateurs, respectivement:



val mergeAdapter = MergeAdapter(headerAdapter, listAdapter, footerAdapter)
recyclerView.adapter = mergeAdapter


La cellule du haut et celle du bas utilisent la même disposition, la même logique ViewHolder et UI (afficher l'état de chargement et masquer). En général, il suffirait d'utiliser 2 instances du même adaptateur pour le haut et le bas de la liste. Un exemple peut être trouvé ici ou ici .



En bref, d'une manière aussi simple, vous pouvez améliorer le code de votre projet si vous utilisez un adaptateur complexe avec différents types de cellules.



Avez-vous aimé l'article? N'oubliez pas de nous rejoindre sur Telegram , et sur la plate- forme AndroidSchool.ru , des documents utiles pour les développeurs Android et des didacticiels modernes sont publiés.



All Articles