Saya menggunakan pustaka dukungan baru ListAdapter
. Ini kode saya untuk adaptor
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)
}
}
}
Saya memperbarui adaptor dengan
musicViewModel.getAllArtists().observe(this, Observer {
it?.let {
artistAdapter.submitList(it)
}
})
Kelas diff saya
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
}
}
Apa yang terjadi adalah ketika submitList dipanggil pertama kali adaptor merender semua item, tetapi ketika submitList dipanggil lagi dengan properti objek yang diperbarui itu tidak merender ulang tampilan yang telah berubah.
Ini membuat ulang tampilan saat saya menggulir daftar, yang pada gilirannya memanggil bindView()
Juga, saya perhatikan bahwa memanggil adapter.notifyDatasSetChanged()
setelah mengirimkan daftar merender tampilan dengan nilai yang diperbarui, tetapi saya tidak ingin memanggil notifyDataSetChanged()
karena adaptor daftar memiliki utilitas diff built-in
Adakah yang bisa membantu saya di sini?
android
kotlin
listadapter
Veeresh Charantimath
sumber
sumber
ArtistsDiff
dan dengan demikian implementasiArtist
itu sendiri.Jawaban:
Sunting: Saya mengerti mengapa ini terjadi bukan itu maksud saya. Maksud saya adalah setidaknya perlu memberi peringatan atau memanggil
notifyDataSetChanged()
fungsi. Karena ternyata saya memanggilsubmitList(...)
fungsi tersebut karena suatu alasan. Saya cukup yakin orang mencoba mencari tahu apa yang salah selama berjam-jam sampai mereka mengetahui submitList () mengabaikan panggilan secara diam-diam.Ini karena
Google
logika yang aneh. Jadi jika Anda meneruskan daftar yang sama ke adaptor itu bahkan tidak memanggilDiffUtil
.public void submitList(final List<T> newList) { if (newList == mList) { // nothing to do return; } .... }
Saya benar-benar tidak memahami keseluruhan poin ini
ListAdapter
jika tidak dapat menangani perubahan pada daftar yang sama. Jika Anda ingin mengubah item pada daftar yang Anda teruskan keListAdapter
dan melihat perubahannya, Anda perlu membuat salinan dalam dari daftar atau Anda perlu menggunakan regularRecyclerView
denganDiffUtill
kelas Anda sendiri .sumber
submitList
, bukan? Setidaknya harus memanggilnotifyDataSetChanged()
alih - alih mengabaikan panggilan secara diam-diam. Saya cukup yakin orang-orang mencoba untuk mencari tahu apa yang salah selama berjam-jam sampai mereka mengetahuisubmitList()
panggilan mengabaikan diam-diam.RecyclerView.Adapter<VH>
dannotifyDataSetChanged()
. LIfe baik sekarang. Jumlah jam yang terbuangnotifyDataSetChanged()
mahal dan akan benar-benar mengalahkan tujuan memiliki implementasi berbasis DiffUtil. Anda mungkin berhati-hati dan berniat memanggilsubmitList
hanya dengan data baru, tapi sebenarnya itu hanya jebakan kinerja.Pustaka mengasumsikan Anda menggunakan Room atau ORM lain yang menawarkan daftar asinkron baru setiap kali diperbarui, jadi hanya memanggil submitList di atasnya akan berfungsi, dan untuk pengembang yang ceroboh, itu mencegah melakukan penghitungan dua kali jika daftar yang sama dipanggil.
Jawaban yang diterima benar, menawarkan penjelasan tetapi bukan solusi.
Apa yang dapat Anda lakukan jika Anda tidak menggunakan perpustakaan semacam itu adalah:
submitList(null); submitList(myList);
Solusi lain adalah mengganti submitList (yang tidak menyebabkan kedipan cepat itu) seperti:
@Override public void submitList(final List<Author> list) { super.submitList(list != null ? new ArrayList<>(list) : null); }
Atau dengan kode Kotlin:
override fun submitList(list: List<CatItem>?) { super.submitList(list?.let { ArrayList(it) }) }
Logika yang dipertanyakan tetapi bekerja dengan sempurna. Metode pilihan saya adalah yang kedua karena tidak menyebabkan setiap baris mendapatkan panggilan onBind.
sumber
.submitList(new ArrayList(list))
dengan Kotlin hanya Anda perlu mengonversi daftar Anda ke MutableList baru seperti ini atau jenis daftar lain sesuai dengan penggunaan Anda
.observe(this, Observer { adapter.submitList(it?.toMutableList()) })
sumber
Saya memiliki masalah yang sama tetapi rendering yang salah disebabkan oleh kombinasi
setHasFixedSize(true)
danandroid:layout_height="wrap_content"
. Untuk pertama kalinya adaptor diberikan dengan daftar kosong sehingga ketinggian tidak pernah diperbarui dan sebelumnya0
. Bagaimanapun, ini memperbaiki masalah saya. Orang lain mungkin memiliki masalah yang sama dan akan mengira itu masalah pada adaptor.sumber
Hari ini saya juga menemukan "masalah" ini. Dengan bantuan jawaban insa_c dan solusi RJFares, saya menjadikan diri saya fungsi ekstensi 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) }
yang kemudian dapat digunakan di mana saja, misalnya:
viewModel.foundDevices.observe(this, Observer { binding.recyclerViewDevices.adapter.updateList(it) })
dan hanya (dan selalu) menyalin daftar jika sama dengan yang dimuat saat ini.
sumber
Jika Anda mengalami beberapa masalah saat menggunakan
recycler_view.setHasFixedSize(true)
Anda harus memeriksa komentar ini dengan jelas: https://github.com/thoughtbot/expandable-recycler-view/issues/53#issuecomment-362991531
Itu memecahkan masalah di pihak saya.
(Ini tangkapan layar dari komentar seperti yang diminta)
sumber
Menurut dokumen resmi :
Setiap kali Anda memanggil submitList itu mengirimkan daftar baru untuk di-diff dan ditampilkan.
Inilah sebabnya mengapa setiap kali Anda memanggil submitList pada sebelumnya (daftar yang sudah dikirimkan), itu tidak menghitung Diff dan tidak memberi tahu adaptor untuk perubahan dalam dataset.
sumber
Dalam kasus saya, saya lupa menyetel
LayoutManager
untukRecyclerView
. Efeknya sama seperti yang dijelaskan di atas.sumber
Bagi saya, masalah ini muncul jika saya menggunakan
RecyclerView
bagian dalamScrollView
dengannestedScrollingEnabled="false"
dan ketinggian RV disetel kewrap_content
.Adaptor diperbarui dengan benar dan fungsi bind dipanggil, tetapi item tidak ditampilkan - file
RecyclerView
macet di ukuran aslinya.Mengubah
ScrollView
untukNestedScrollView
memperbaiki masalah.sumber
Saya punya masalah serupa. Masalahnya ada pada
Diff
fungsi, yang tidak cukup membandingkan item. Siapapun dengan masalah ini, pastikanDiff
fungsi Anda (dan dengan ekstensi kelas objek data Anda) berisi definisi perbandingan yang tepat - yaitu membandingkan semua bidang yang mungkin diperbarui dalam item baru. Misalnya di postingan aslinyaoverride fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem }
Fungsi ini (berpotensi) tidak melakukan apa yang tertulis di label: fungsi ini tidak membandingkan konten dari dua item - kecuali Anda telah mengganti
equals()
fungsi diArtist
kelas. Dalam kasus saya, saya belum melakukannya, dan definisiareContentsTheSame
hanya memeriksa salah satu bidang yang diperlukan, karena pengawasan saya saat menerapkannya. Ini adalah persamaan struktural vs. persamaan referensial, Anda dapat menemukannya lebih lanjut di sinisumber
Untuk siapa saja yang memiliki skenario yang sama dengan saya, saya meninggalkan solusi saya, yang saya tidak tahu mengapa itu berhasil, di sini.
Solusi yang berhasil untuk saya adalah dari @Mina Samir, yang mengirimkan daftar sebagai daftar yang bisa diubah.
Skenario Masalah Saya:
-Memuat daftar teman di dalam sebuah fragmen.
ActivityMain melampirkan FragmentFriendList (Mengamati liveata item db teman) dan pada saat yang sama, meminta permintaan http ke server untuk mendapatkan semua daftar teman saya.
Perbarui atau masukkan item dari server http.
Setiap perubahan memicu callback onChanged dari Liveata. Tapi, ketika ini pertama kalinya saya meluncurkan aplikasi, yang berarti tidak ada apa-apa di tabel saya, submitList berhasil tanpa kesalahan apa pun, tetapi tidak ada yang muncul di layar.
Namun, saat ini kedua kalinya saya meluncurkan aplikasi, data sedang dimuat ke layar.
Solusinya adalah, seperti yang disebutkan di atas, mengirimkan daftar sebagai mutableList.
sumber
Alasan ListAdapter .submitlist Anda tidak dipanggil adalah karena objek yang Anda perbarui masih menyimpan alamat yang sama di memori.
Ketika Anda memperbarui objek dengan katakanlah .setText itu mengubah nilai dalam objek aslinya.
Sehingga ketika Anda memeriksa apakah object.id == object2.id itu akan kembali sama karena keduanya memiliki referensi ke lokasi yang sama di memori.
Solusinya adalah membuat objek baru dengan data yang diperbarui dan memasukkannya ke dalam daftar Anda. Kemudian submitList akan dipanggil dan itu akan bekerja dengan benar
sumber
Saya perlu memodifikasi DiffUtils saya
override fun areContentsTheSame(oldItem: Vehicle, newItem: Vehicle): Boolean {
Untuk benar-benar mengembalikan apakah isinya baru, tidak hanya membandingkan id model.
sumber
Menggunakan @RJFares jawaban pertama berhasil memperbarui daftar, tetapi tidak mempertahankan status gulir. Keseluruhan
RecyclerView
dimulai dari posisi ke-0. Sebagai solusinya, inilah yang saya lakukan: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 }
Dengan cara ini, saya dapat mempertahankan status gulir
RecyclerView
bersama dengan data yang diubah.sumber