RecyclerView में आइटम अद्यतन नहीं कर रहा है


93

मैं नए समर्थन पुस्तकालय का उपयोग कर रहा हूं ListAdapter। यहाँ एडेप्टर के लिए मेरा कोड है

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)
        }
    }
}

मैं एडाप्टर के साथ अद्यतन कर रहा हूँ

musicViewModel.getAllArtists().observe(this, Observer {
            it?.let {
                artistAdapter.submitList(it)
            }
        })

मेरी अलग क्लास

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
    }
}

क्या हो रहा है जब submitList को पहली बार कहा जाता है कि एडाप्टर सभी वस्तुओं को प्रदान करता है, लेकिन जब submitList को फिर से अद्यतन वस्तु गुणों के साथ बुलाया जाता है तो यह उस दृश्य को फिर से प्रस्तुत नहीं करता है जो बदल गया है।

यह सूची को स्क्रॉल करता है, जो बदले में कॉल करता है, यह फिर से प्रस्तुत करता है bindView()

इसके अलावा, मैंने देखा है कि adapter.notifyDatasSetChanged()सबमिट सूची के बाद कॉल करना अद्यतन मूल्यों के साथ दृश्य प्रस्तुत करता है, लेकिन मैं कॉल नहीं करना चाहता notifyDataSetChanged()क्योंकि सूची एडॉप्टर में अलग-अलग बर्तन हैं।

क्या कोई यहां मेरी सहायता करेगा?


समस्या ArtistsDiffइस प्रकार से संबंधित हो सकती है और इस प्रकार Artistस्वयं के कार्यान्वयन से संबंधित है।
tynn

हां, मैं भी ऐसा ही सोचता हूं, लेकिन मैं इसे इंगित नहीं कर सकता हूं
वीरेश चारंतीमथ

आप इसे डीबग कर सकते हैं या लॉग स्टेटमेंट जोड़ सकते हैं। इसके अलावा, आप प्रश्न में संबंधित कोड जोड़ सकते हैं।
tynn

इस प्रश्न की भी जाँच करें, मैंने इसे अलग तरीके से हल किया है stackoverflow.com/questions/58232606/…
MisterCat

जवाबों:


108

संपादित करें: मैं समझता हूं कि ऐसा क्यों होता है जो मेरी बात नहीं थी। मेरा कहना है कि इसे कम से कम चेतावनी देने या notifyDataSetChanged()फ़ंक्शन को कॉल करने की आवश्यकता है। क्योंकि जाहिरा तौर पर मैं submitList(...)एक कारण के लिए फ़ंक्शन कह रहा हूं । मुझे पूरा यकीन है कि लोग यह जानने की कोशिश कर रहे हैं कि घंटों तक क्या गलत हुआ जब तक वे सबमिट नहीं करते हैं () चुपचाप कॉल को अनदेखा कर देता है।

यह Googleअजीब तर्क के कारण है। इसलिए यदि आप उसी सूची को एडॉप्टर से पास करते हैं तो वह कॉल भी नहीं करता है DiffUtil

public void submitList(final List<T> newList) {
    if (newList == mList) {
        // nothing to do
        return;
    }
....
}

मैं वास्तव में इस के पूरे बिंदु को नहीं समझता ListAdapterअगर यह एक ही सूची में परिवर्तन को संभाल नहीं सकता है। यदि आप उस सूची पर आइटम बदलना चाहते हैं जिसे आप पास करते हैं ListAdapterऔर परिवर्तन देखते हैं तो या तो आपको सूची की एक गहरी प्रतिलिपि बनाने की आवश्यकता है या आपको RecyclerViewअपनी DiffUtillकक्षा के साथ नियमित रूप से उपयोग करने की आवश्यकता है ।


5
क्योंकि यह पिछले राज्य के लिए आवश्यक है कि वह प्रदर्शन करे। यदि आप पिछली अवस्था को अधिलेखित करते हैं, तो बेशक इसे संभाल नहीं सकते। O_o
EpicPandaForce

34
हां, लेकिन उस बिंदु पर, एक कारण है कि मैं submitListसही क्यों कहता हूं ? यह कम से कम notifyDataSetChanged()चुपचाप कॉल को अनदेखा करने के बजाय कॉल करना चाहिए । मुझे पूरा यकीन है कि लोग यह जानने की कोशिश कर रहे हैं कि घंटों तक गलत क्या हुआ जब तक कि वे submitList()चुपचाप कॉल को नजरअंदाज नहीं करते।
insa_c

6
इसलिए मैं वापस करने के लिए कर रहा हूँ RecyclerView.Adapter<VH>और notifyDataSetChanged()। LIfe अब अच्छा है। व्यर्थ की अच्छी संख्या में घंटे
उदयित्य बरुआ

1
@insa_c आप अपनी गिनती में 3 घंटे जोड़ सकते हैं, यही कारण है कि मैं यह समझने की कोशिश कर रहा हूं कि मेरी सूची कुछ बढ़त के मामलों में अपडेट क्यों नहीं हो रही है ...
बेनक्री

1
notifyDataSetChanged()महंगा है और एक डिफ्यूटल आधारित कार्यान्वयन होने के बिंदु को पूरी तरह से हरा देगा। आप submitListकेवल नए डेटा के साथ कॉल करने में सावधान और इरादतन हो सकते हैं , लेकिन वास्तव में यह केवल एक प्रदर्शन जाल है।
डेविड लियू

63

लाइब्रेरी मानती है कि आप कक्ष या किसी अन्य ORM का उपयोग कर रहे हैं जो हर बार अपडेट होने के बाद एक नई async सूची प्रदान करता है, इसलिए बस उस पर सबमिटलिस्ट काम करेगा, और मैला डेवलपर्स के लिए, यह एक ही सूची को कॉल करने पर दो बार गणना करने से रोकता है।

स्वीकृत उत्तर सही है, यह स्पष्टीकरण प्रदान करता है लेकिन समाधान नहीं।

यदि आप ऐसी किसी लाइब्रेरी का उपयोग नहीं कर रहे हैं तो आप क्या कर सकते हैं:

submitList(null);
submitList(myList);

एक अन्य समाधान सबमिटलिस्ट को ओवरराइड करना होगा (जो उस त्वरित ब्लिंक का कारण नहीं बनता है) जैसे:

@Override
public void submitList(final List<Author> list) {
    super.submitList(list != null ? new ArrayList<>(list) : null);
}

या कोटलिन कोड के साथ:

override fun submitList(list: List<CatItem>?) {
    super.submitList(list?.let { ArrayList(it) })
}

संदिग्ध तर्क लेकिन पूरी तरह से काम करता है। मेरा पसंदीदा तरीका दूसरा है क्योंकि इससे प्रत्येक पंक्ति को ऑनबाइंड कॉल प्राप्त नहीं होती है।


7
वह हैक है। बस सूची की एक प्रति पास करें। .submitList(new ArrayList(list))
पॉल Woitaschek

3
मैंने आखिरी घंटे बिताए हैं यह जानने की कोशिश कर रहा हूं कि समस्या मेरे तर्क के साथ क्या है। ऐसा अजीब तर्क।
जेरी ओकॉर्फ

7
@PaulWoitaschek यह हैक नहीं है, यह JAVA का उपयोग कर रहा है :) इसका उपयोग पुस्तकालयों में कई समस्याओं को ठीक करने के लिए किया जाता है जहां डेवलपर "सो रहा है"। पास करने के बजाय आप इसे क्यों चुनते हैं इसका कारण .submitList (नया ArrayList (सूची)) है क्योंकि आप अपने कोड में कई स्थानों पर सूचियाँ जमा कर सकते हैं। आप हर बार एक नई सरणी बनाना भूल सकते हैं, इसीलिए आप ओवरराइड करते हैं।
RJFares

2
यहां तक ​​कि कमरे का उपयोग करने के साथ मैं एक समान मुद्दे में चल रहा हूं।
बिंक

2
स्पष्ट रूप से यह नए आइटम के साथ व्यूमोडेल में एक सूची को अपडेट करते समय काम करता है, लेकिन जब मैं किसी सूची में किसी आइटम की संपत्ति (बूलियन - iselected) को अपडेट करता हूं तो यह अभी भी काम नहीं करेगा .. Idk क्यों लेकिन DiffUtil उसी पुराने और नए आइटम को वापस लौटाता है जैसा कि मैं ' जाँच की गई। किसी भी विचार जहां समस्या हो सकती है?
राल्फ

23

कोटलिन के साथ आपको अपनी सूची को अपने उपयोग के अनुसार इस तरह के या अन्य प्रकार की सूची में नए MutableList में बदलने की आवश्यकता है

.observe(this, Observer {
            adapter.submitList(it?.toMutableList())
        })

यह अजीब है, लेकिन सूची को मेरे लिए उत्परिवर्ती कार्यों में परिवर्तित करना। धन्यवाद!
Nhon गुयेन

4
यह काम क्यों कर रहा है? यह काम करता है लेकिन बहुत उत्सुक है कि ऐसा क्यों होता है।
March3April4

मेरी राय में, ListAdapter को आपकी सूची के संदर्भ के बारे में नहीं होना चाहिए, इसलिए इसे समझें? .toMutableList () आप एडाप्टर के लिए एक नई आवृत्ति सूची प्रस्तुत करते हैं। मुझे उम्मीद है कि आपके लिए पर्याप्त स्पष्ट है। @ March3April4
मीना समीर

धन्यवाद। आपकी टिप्पणी के अनुसार, मैंने अनुमान लगाया कि लिस्ट एडॉप्टर को सूची <टी> के रूप में डेटासेट प्राप्त होता है, जो एक परिवर्तनशील सूची या एक अपरिवर्तनीय सूची भी हो सकती है। यदि मैं एक अपरिवर्तनीय सूची से बाहर निकलता हूं, तो मैंने जो परिवर्तन किए हैं, उन्हें डेटासेट द्वारा अवरुद्ध किया जा रहा है, लिस्ट एडेप्टर द्वारा नहीं।
March3April4

मुझे लगता है कि आपको यह @ March3April4 मिल गया है, इसके अलावा, तंत्र के बारे में परवाह करें कि आप अलग-अलग बर्तनों के साथ उपयोग करते हैं, क्योंकि इसमें जिम्मेदारियों की गणना होगी कि सूची में आइटम बदलना चाहिए या नहीं;)
मीना समीर

10

मेरे पास एक समान समस्या थी लेकिन गलत संयोजन setHasFixedSize(true)और के संयोजन के कारण हुआ था android:layout_height="wrap_content"। पहली बार एडॉप्टर को एक खाली सूची के साथ आपूर्ति की गई थी, इसलिए ऊंचाई कभी भी अपडेट नहीं हुई थी और थी 0। वैसे भी, यह मेरे मुद्दे से उलट है। किसी और को भी यही समस्या हो सकती है और वह सोचता है कि यह एडॉप्टर में समस्या है।


1
हाँ, लपेटने के लिए रीसायकल सेट करें। कंटेंट सूची को अपडेट कर देगा, अगर आप इसे मैच_पर सेट करते हैं तो यह एडेप्टर को कॉल नहीं करेगा
एक्सेल स्टैडरलिन

5

आज मैं भी इस "समस्या" पर अड़ गया। Insa_c के उत्तर और RJFares के समाधान की मदद से मैंने खुद को कोटलिन एक्सटेंशन फ़ंक्शन बनाया:

/**
 * 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)
}

जो तब कहीं भी इस्तेमाल किया जा सकता है, जैसे:

viewModel.foundDevices.observe(this, Observer {
    binding.recyclerViewDevices.adapter.updateList(it)
})

और यह केवल (और हमेशा) सूची को कॉपी करता है यदि यह वर्तमान में लोड किए गए समान है।


5

यदि आप उपयोग करते समय कुछ मुद्दों का सामना करते हैं

recycler_view.setHasFixedSize(true)

आपको निश्चित रूप से इस टिप्पणी की जाँच करनी चाहिए: https://github.com/thoughtbot/expandable-recycler-view/issues/53#issuecomment-362991531

इसने मेरी तरफ से मसला हल कर दिया।

(यहाँ अनुरोध के अनुसार टिप्पणी का एक स्क्रीनशॉट है)

यहाँ छवि विवरण दर्ज करें


किसी समाधान का लिंक स्वागत योग्य है, लेकिन कृपया सुनिश्चित करें कि आपका उत्तर इसके बिना उपयोगी है: लिंक के चारों ओर संदर्भ जोड़ें ताकि आपके साथी उपयोगकर्ताओं को यह पता चले कि यह क्या है और यह क्यों है, तो पृष्ठ के सबसे प्रासंगिक हिस्से को उद्धृत करें ' मामले में लक्ष्य पृष्ठ अनुपलब्ध होने पर पुनः लिंक करना।
मुस्तफा अरियन नजद

3

आधिकारिक डॉक्स के अनुसार :

जब भी आप सबमिट करें कॉल करें यह अलग और प्रदर्शित होने के लिए एक नई सूची सबमिट करता है ।

यही कारण है कि जब भी आप पिछली (पहले से प्रस्तुत सूची) पर सबमिटलिस्ट कहते हैं , तो यह डिफ की गणना नहीं करता है और डेटासेट में बदलाव के लिए एडेप्टर को सूचित नहीं करता है ।


3

मेरे मामले में मैं के लिए सेट करने के LayoutManagerलिए भूल गया RecyclerView। उस का प्रभाव ऊपर वर्णित के समान है।


2

मेरे लिए, यह मुद्दा दिखाई दिया अगर मैं RecyclerViewअंदर और आरवी ऊंचाई सेट के ScrollViewसाथ उपयोग कर रहा था । एडेप्टर ठीक से अपडेट हुआ और बाइंड फ़ंक्शन को कॉल किया गया, लेकिन आइटम नहीं दिखाए गए - एnestedScrollingEnabled="false"wrap_content
RecyclerView अपने मूल आकार में अटक गया था।

मुद्दे ScrollViewको NestedScrollViewतय करने के लिए बदल रहा है ।


2

मुझे भी ऐसी ही समस्या का समाधान करना पड़ा था। यह समस्या Diffफ़ंक्शंस में थी, जिसमें आइटमों की पर्याप्त रूप से तुलना नहीं की गई थी। इस समस्या के साथ, सुनिश्चित करें कि आपके Diffफ़ंक्शंस (और आपके डेटा ऑब्जेक्ट क्लासेस द्वारा) में उचित तुलना परिभाषाएँ हैं - यानी सभी फ़ील्ड्स की तुलना करना जो नए आइटम में अपडेट किए जा सकते हैं। उदाहरण के लिए मूल पोस्ट में

    override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
    return oldItem == newItem
}

यह फ़ंक्शन (संभावित) ऐसा नहीं करता है जो लेबल पर कहता है: यह दो वस्तुओं की सामग्री की तुलना नहीं करता है - जब तक कि आपने कक्षा equals()में फ़ंक्शन को ओवरराइड नहीं किया है Artist। मेरे मामले में, मेरे पास नहीं था, और areContentsTheSameइसे लागू करते समय मेरी निगरानी के कारण, केवल एक आवश्यक फ़ील्ड की जाँच की गई थी। यह संरचनात्मक समानता बनाम संदर्भात्मक समानता है, आप इसके बारे में अधिक जानकारी यहां पा सकते हैं


1

जिस किसी का परिदृश्य मेरे जैसा है, मैं अपना समाधान छोड़ता हूं, जो मुझे नहीं पता कि यह क्यों काम कर रहा है, यहां।

मेरे लिए काम करने वाला समाधान @ मीना समीर से था, जो सूची को एक परिवर्तनशील सूची के रूप में प्रस्तुत कर रहा है।

मेरा मुद्दा परिदृश्य:

एक टुकड़े के अंदर एक मित्र सूची का निर्माण।

  1. एक्टिविटी मेन फ्रैगमेंटफ्रेंडलिस्ट (मित्र डीबी आइटम के लाइवटाटा के लिए अवलोकन) को संलग्न करता है और उसी समय, सर्वर से http अनुरोध करता है कि मेरी सभी मित्र सूची प्राप्त करें।

  2. Http सर्वर से आइटम को अपडेट करें या डालें।

  3. प्रत्येक परिवर्तन, लिवटा के ऑन-कॉल किए गए कॉलबैक को प्रज्वलित करता है। लेकिन, जब यह मेरा पहली बार एप्लिकेशन लॉन्च कर रहा है, जिसका अर्थ है कि मेरी मेज पर कुछ भी नहीं था, तो सबमिटलिस्ट किसी भी तरह की त्रुटि के बिना सफल होता है, लेकिन स्क्रीन पर कुछ भी दिखाई नहीं देता है।

  4. हालाँकि, जब यह एप्लिकेशन लॉन्च करने का मेरा दूसरा मौका है, तो डेटा को स्क्रीन पर लोड किया जा रहा है।

समाधान है, जैसा कि ऊपर उल्‍लेख किया गया है, सूची को एक म्‍युटेबलिस्ट के रूप में सबमिट किया गया है।


1

आपके ListAdapter .submitlist को नहीं कहा जाता है क्योंकि आपने जिस वस्तु को अद्यतन किया है वह अभी भी स्मृति में एक ही पता रखती है।

जब आप किसी ऑब्जेक्ट को अद्यतन करने देते हैं तो .setText इसे मूल ऑब्जेक्ट में मान बदल देता है।

ताकि जब आप जाँच करें कि क्या object.id == object2.id पर तो यह समान है क्योंकि दोनों में मेमोरी में समान स्थान का संदर्भ है।

समाधान अद्यतन डेटा के साथ एक नई वस्तु बनाने और अपनी सूची में सम्मिलित करने के लिए है। फिर सबमिटलिस्ट को कॉल किया जाएगा और यह सही तरीके से काम करेगा


0

मुझे अपने डिफुटिल्स को संशोधित करने की आवश्यकता थी

override fun areContentsTheSame(oldItem: Vehicle, newItem: Vehicle): Boolean {

वास्तव में लौटने के लिए कि क्या सामग्री नई है, न कि केवल मॉडल की आईडी की तुलना करें।


0

@RJFares का उपयोग करते हुए पहले उत्तर सूची को सफलतापूर्वक अपडेट करता है, लेकिन स्क्रॉल स्थिति को बनाए नहीं रखता है। पूरी RecyclerView0 स्थिति से शुरू होती है। वर्कअराउंड के रूप में, मैंने यही किया है:

   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

   }

इस तरह, मैं RecyclerViewसंशोधित डेटा के साथ स्क्रॉल स्थिति बनाए रखने में सक्षम हूं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.