J'utilise la nouvelle bibliothèque de support ListAdapter
. Voici mon code pour l'adaptateur
class ArtistsAdapter : ListAdapter<Artist, ArtistsAdapter.ViewHolder>(ArtistsDiff()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(parent.inflate(R.layout.item_artist))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(artist: Artist) {
itemView.artistDetails.text = artist.artistAlbums
.plus(" Albums")
.plus(" \u2022 ")
.plus(artist.artistTracks)
.plus(" Tracks")
itemView.artistName.text = artist.artistCover
itemView.artistCoverImage.loadURL(artist.artistCover)
}
}
}
Je mets à jour l'adaptateur avec
musicViewModel.getAllArtists().observe(this, Observer {
it?.let {
artistAdapter.submitList(it)
}
})
Ma classe de diff
class ArtistsDiff : DiffUtil.ItemCallback<Artist>() {
override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem?.artistId == newItem?.artistId
}
override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem == newItem
}
}
Ce qui se passe, c'est lorsque submitList est appelé la première fois que l'adaptateur rend tous les éléments, mais lorsque submitList est à nouveau appelé avec des propriétés d'objet mises à jour, il ne rend pas la vue qui a changé.
Il restitue la vue lorsque je fais défiler la liste, qui à son tour appelle bindView()
En outre, j'ai remarqué que l'appel adapter.notifyDatasSetChanged()
après la liste de soumission rend la vue avec des valeurs mises à jour, mais je ne veux pas appeler notifyDataSetChanged()
car l'adaptateur de liste a des utilitaires différentiels intégrés
Quelqu'un peut-il m'aider?
android
kotlin
listadapter
Veeresh Charantimath
la source
la source
ArtistsDiff
et donc à sa mise en œuvreArtist
.Réponses:
Edit: Je comprends pourquoi cela arrive, ce n'était pas mon propos. Mon point est qu'il doit au moins donner un avertissement ou appeler la
notifyDataSetChanged()
fonction. Parce qu'apparemment j'appelle lasubmitList(...)
fonction pour une raison. Je suis sûr que les gens essaient de comprendre ce qui n'a pas fonctionné pendant des heures jusqu'à ce qu'ils découvrent que submitList () ignore silencieusement l'appel.Ceci est dû à
Google
une logique étrange. Donc, si vous passez la même liste à l'adaptateur, il n'appelle même pas leDiffUtil
.public void submitList(final List<T> newList) { if (newList == mList) { // nothing to do return; } .... }
Je ne comprends vraiment pas tout l'intérêt de cela
ListAdapter
s'il ne peut pas gérer les changements sur la même liste. Si vous souhaitez modifier les éléments de la liste que vous passez àListAdapter
et voir les modifications, vous devez soit créer une copie complète de la liste, soit utiliser RegularRecyclerView
avec votre propreDiffUtill
classe.la source
submitList
, non? Il devrait au moins appeler lenotifyDataSetChanged()
au lieu d'ignorer silencieusement l'appel. Je suis presque sûr que les gens essaient de comprendre ce qui n'a pas fonctionné pendant des heures jusqu'à ce qu'ils découvrent qu'ilssubmitList()
ignorent silencieusement l'appel.RecyclerView.Adapter<VH>
etnotifyDataSetChanged()
. La vie est bonne maintenant. Bon nombre d'heures gaspillénotifyDataSetChanged()
est coûteux et irait complètement à l'encontre de l'intérêt d'une implémentation basée sur DiffUtil. Vous pouvez être prudent et attentif en n'appelantsubmitList
qu'avec de nouvelles données, mais en réalité, ce n'est qu'un piège de performance.La bibliothèque suppose que vous utilisez Room ou tout autre ORM qui offre une nouvelle liste asynchrone à chaque fois qu'elle est mise à jour, donc appeler simplement submitList dessus fonctionnera, et pour les développeurs bâclés, cela empêche de faire les calculs deux fois si la même liste est appelée.
La réponse acceptée est correcte, elle offre l'explication mais pas la solution.
Ce que vous pouvez faire si vous n'utilisez pas de telles bibliothèques est:
submitList(null); submitList(myList);
Une autre solution serait de remplacer submitList (qui ne provoque pas ce clignotement rapide) comme tel:
@Override public void submitList(final List<Author> list) { super.submitList(list != null ? new ArrayList<>(list) : null); }
Ou avec le code Kotlin:
override fun submitList(list: List<CatItem>?) { super.submitList(list?.let { ArrayList(it) }) }
Logique discutable mais fonctionne parfaitement. Ma méthode préférée est la deuxième car elle ne provoque pas chaque ligne pour obtenir un appel onBind.
la source
.submitList(new ArrayList(list))
avec Kotlin, il vous suffit de convertir votre liste en une nouvelle MutableList comme celle-ci ou un autre type de liste en fonction de votre utilisation
.observe(this, Observer { adapter.submitList(it?.toMutableList()) })
la source
J'ai eu un problème similaire mais le rendu incorrect a été causé par une combinaison de
setHasFixedSize(true)
etandroid:layout_height="wrap_content"
. Pour la première fois, l'adaptateur a été fourni avec une liste vide de sorte que la hauteur n'a jamais été mise à jour et l'était0
. Quoi qu'il en soit, cela a résolu mon problème. Quelqu'un d'autre pourrait avoir le même problème et pensera qu'il s'agit d'un problème dans l'adaptateur.la source
Si vous rencontrez des problèmes lors de l'utilisation
recycler_view.setHasFixedSize(true)
vous devriez certainement vérifier ce commentaire: https://github.com/thoughtbot/expandable-recycler-view/issues/53#issuecomment-362991531
Cela a résolu le problème de mon côté.
(Voici une capture d'écran du commentaire comme demandé)
la source
Aujourd'hui, je suis également tombé sur ce "problème". Avec l'aide de la réponse d' insa_c et de la solution RJFares je me suis créé une fonction d'extension Kotlin:
/** * Update the [RecyclerView]'s [ListAdapter] with the provided list of items. * * Originally, [ListAdapter] will not update the view if the provided list is the same as * currently loaded one. This is by design as otherwise the provided DiffUtil.ItemCallback<T> * could never work - the [ListAdapter] must have the previous list if items to compare new * ones to using provided diff callback. * However, it's very convenient to call [ListAdapter.submitList] with the same list and expect * the view to be updated. This extension function handles this case by making a copy of the * list if the provided list is the same instance as currently loaded one. * * For more info see 'RJFares' and 'insa_c' answers on * /programming/49726385/listadapter-not-updating-item-in-reyclerview */ fun <T, VH : RecyclerView.ViewHolder> ListAdapter<T, VH>.updateList(list: List<T>?) { // ListAdapter<>.submitList() contains (stripped): // if (newList == mList) { // // nothing to do // return; // } this.submitList(if (list == this.currentList) list.toList() else list) }
qui peut ensuite être utilisé n'importe où, par exemple:
viewModel.foundDevices.observe(this, Observer { binding.recyclerViewDevices.adapter.updateList(it) })
et il ne copie (et toujours) la liste que si elle est la même que celle actuellement chargée.
la source
Selon les documents officiels :
Chaque fois que vous appelez submitList, il soumet une nouvelle liste à comparer et à afficher.
C'est pourquoi chaque fois que vous appelez submitList sur la liste précédente (liste déjà soumise), il ne calcule pas le Diff et ne notifie pas l'adaptateur en cas de modification de l'ensemble de données.
la source
Pour moi, ce problème est apparu si j'utilisais à l'
RecyclerView
intérieur deScrollView
avecnestedScrollingEnabled="false"
et la hauteur RV définie surwrap_content
.L'adaptateur s'est mis à jour correctement et la fonction de liaison a été appelée, mais les éléments n'ont pas été affichés - le
RecyclerView
était bloqué à sa «taille d'origine.Passer
ScrollView
àNestedScrollView
résoudre le problème.la source
Dans mon cas, j'ai oublié de définir le
LayoutManager
pour leRecyclerView
. L'effet est le même que celui décrit ci-dessus.la source
Pour tous ceux dont le scénario est le même que le mien, je laisse ma solution, dont je ne sais pas pourquoi elle fonctionne, ici.
La solution qui a fonctionné pour moi était de @Mina Samir, qui soumet la liste sous forme de liste modifiable.
Mon scénario de problème:
-Chargement d'une liste d'amis dans un fragment.
ActivityMain attache la FragmentFriendList (observe les données de vie des éléments de base de données ami) et en même temps, demande une requête http au serveur pour obtenir toute ma liste d'amis.
Mettez à jour ou insérez les éléments du serveur http.
Chaque changement déclenche le rappel onChanged de la liveata. Mais, quand c'est la première fois que je lance l'application, ce qui signifie qu'il n'y avait rien sur ma table, la submitList réussit sans aucune erreur d'aucune sorte, mais rien n'apparaît à l'écran.
Cependant, lorsque c'est la deuxième fois que je lance l'application, les données sont chargées à l'écran.
La solution est, comme indiqué ci-dessus, de soumettre la liste en tant que mutableList.
la source
J'avais un problème similaire. Le problème était dans les
Diff
fonctions, qui ne comparaient pas correctement les éléments. Toute personne ayant ce problème, assurez-vous que vosDiff
fonctions (et par extension vos classes d'objets de données) contiennent des définitions de comparaison appropriées - c'est-à-dire en comparant tous les champs qui pourraient être mis à jour dans le nouvel élément. Par exemple dans le message d'origineoverride fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem }
Cette fonction (potentiellement) ne fait pas ce qu'elle dit sur l'étiquette: elle ne compare pas le contenu des deux éléments - sauf si vous avez remplacé la
equals()
fonction dans laArtist
classe. Dans mon cas, je ne l'avais pas fait, et la définition deareContentsTheSame
n'a coché qu'un des champs nécessaires, en raison de mon oubli lors de sa mise en œuvre. C'est l'égalité structurelle vs l'égalité référentielle, vous pouvez en savoir plus icila source
J'avais besoin de modifier mes DiffUtils
override fun areContentsTheSame(oldItem: Vehicle, newItem: Vehicle): Boolean {
Pour retourner réellement si le contenu est nouveau, pas simplement comparer l'id du modèle.
la source
L'utilisation de la première réponse @RJFares met à jour la liste avec succès, mais ne conserve pas l'état de défilement. L'ensemble
RecyclerView
commence à partir de la 0ème position. Pour contourner ce problème, voici ce que j'ai fait:fun updateDataList(newList:List<String>){ //new list from DB or Network val tempList = dataList.toMutableList() // dataList is the old list tempList.addAll(newList) listAdapter.submitList(tempList) // Recyclerview Adapter Instance dataList = tempList }
De cette façon, je suis capable de maintenir l'état de défilement
RecyclerView
avec les données modifiées.la source