RecyclerView में डेटा ताज़ा करना और उसकी स्क्रॉल स्थिति को बनाए रखना


96

कोई व्यक्ति अपने एडेप्टर में RecyclerViewकॉल किए गए डेटा को कैसे रिफ्रेश करता है ( notifyDataSetChangedऔर यह सुनिश्चित करता है कि स्क्रॉल पोज़िशन बिल्कुल उसी जगह पर रीसेट हो, जहां वह था?

अच्छे ListViewऑल के मामले में getChildAt(0), यह सब प्राप्त कर रहा है , इसकी जाँच कर रहा है getTop()और setSelectionFromTopबाद में उसी सटीक डेटा के साथ कॉल कर रहा है ।

के मामले में यह संभव नहीं लगता है RecyclerView

मुझे लगता है कि मैं इसका उपयोग करने वाला हूं LayoutManagerजो वास्तव में प्रदान करता है scrollToPositionWithOffset(int position, int offset), लेकिन स्थिति और ऑफसेट को प्राप्त करने का उचित तरीका क्या है?

layoutManager.findFirstVisibleItemPosition()और layoutManager.getChildAt(0).getTop()?

या काम करने के लिए एक और अधिक सुरुचिपूर्ण तरीका है?


यह आपके RecyclerView या LayoutManager की चौड़ाई और ऊँचाई मानों के बारे में है। इस समाधान की जाँच करें: stackoverflow.com/questions/28993640/…
serefakyuz

@ कोनार्ड, मेरे मामले में मेरे पास ऑडियो फाइल की सूची है जिसमें सीकबर कार्यान्वयन और निम्नलिखित मुद्दे के साथ चल रहा है: 1) कुछ अंतिम अनुक्रमित आइटम के लिए जैसे ही यह नोटिफाई किया गया ItItemChanged (पॉज़) यह नीचे स्वचालित पर पुश दृश्य। 2) जबकि InformItemChanged (स्थिति) पर रखें यह सूची अटक गई और आसानी से स्क्रॉल करने की अनुमति न दें। हालांकि मैं एक प्रश्न के रूप में पूछूंगा, लेकिन मुझे बताएं कि क्या आपको इस तरह के मुद्दे को ठीक करने के लिए कोई बेहतर तरीका मिला है।
CoDe

जवाबों:


139

मैं इसका उपयोग करता हूं। ^ _ ^

// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);

यह सरल है, आशा है कि यह आपकी मदद करेगा!


1
यह वास्तव में सही उत्तर imo है। यह अतुल्यकालिक डेटा लोडिंग के कारण ही मुश्किल हो जाता है, लेकिन आप बहाली को स्थगित कर सकते हैं।
एपिकपांडाफ़ोर्स

परफेक्ट +2 बिल्कुल वही जो मैं देख रहा था
Adeel Turk

@ बबलट्री पेस्ट .ofcode.org / EEdjSLQbLrcQyxXpBpEQ4m मैं इसे लागू करने की कोशिश कर रहा हूं, लेकिन यह काम नहीं कर रहा है, मैं क्या गलत कर रहा हूं?
कौस्तुभ भागवत

@KaustubhBhagwat शायद आपको उपयोग करने से पहले "recyclerViewState! = Null" की जाँच करनी चाहिए।
डॉनवाई

4
मुझे इस कोड का उपयोग कहां करना चाहिए? onResume विधि
लकी_गर्ल

47

मुझे काफी समान समस्या है। और मैं निम्नलिखित समाधान के साथ आया।

का उपयोग करना notifyDataSetChangedएक बुरा विचार है। आपको अधिक विशिष्ट होना चाहिए, फिर आपके RecyclerViewलिए स्क्रॉल स्थिति को बचाएगा।

उदाहरण के लिए, यदि आपको केवल रिफ्रेश करने की जरूरत है, या दूसरे शब्दों में, आप चाहते हैं कि प्रत्येक दृश्य रिबंड हो जाए, तो बस यही करें:

adapter.notifyItemRangeChanged(0, adapter.getItemCount());

2
मैं adapter.notifyItemRangeChanged (0, adapter.getItemCount ()) ;, वह भी .notifyDataSetChanged () के रूप में काम करता है की कोशिश की
मणि

4
इससे मेरे मामले में मदद मिली। मैं 0 पर कई आइटम सम्मिलित कर रहा था और notifyDataSetChangedस्क्रॉल स्थिति के साथ नए आइटम दिखा रहा था। हालाँकि, अगर स्क्रॉल राज्य का उपयोग notifyItemRangeInserted(0, newItems.size() - 1)करता RecyclerViewहै।
कार्लोस

बहुत अच्छा जवाब, मैं उस जवाब का हल खोज रहा हूँ एक दिन। धन्यवाद, बहुत कुछ ..
गुंडु बंदर 16

adapter.notifyItemRangeChanged (0, adapter.getItemCount ()); टाला जाना चाहिए, जैसा कि नवीनतम Google I / O में इंगित किया गया है (recyclerview (और बाहरी में recyclerview पर प्रस्तुति देखें), यह बेहतर होगा (यदि आपके पास ज्ञान है) recyclerview को यह बताने के लिए कि कौन सा आइटम एक फ्लैट कैच-ऑल रिफ्रेश के बजाय बदल गया है सभी विचारों के।
ए। स्टीनबर्गेन

3
काम नहीं कर रहा है, फिर भी मैं ऊपर
जोड़कर पुनर्नवीनीकरण करता हूं

21

संपादित करें: ठीक उसी स्पष्ट स्थिति को पुनर्स्थापित करने के लिए , जैसा कि यह ठीक वैसा ही बना है, हमें कुछ अलग करने की आवश्यकता है (सटीक स्क्रॉल मूल्य को पुनर्स्थापित करने के लिए नीचे देखें)

इस तरह स्थिति और ऑफसेट सहेजें:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
int firstItem = manager.findFirstVisibleItemPosition();
View firstItemView = manager.findViewByPosition(firstItem);
float topOffset = firstItemView.getTop();

outState.putInt(ARGS_SCROLL_POS, firstItem);
outState.putFloat(ARGS_SCROLL_OFFSET, topOffset);

और फिर इस तरह स्क्रॉल पुनर्स्थापित करें:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);

यह सूची को उसकी सटीक स्पष्ट स्थिति में पुनर्स्थापित करता है । स्पष्ट है क्योंकि यह उपयोगकर्ता के लिए समान दिखेगा, लेकिन इसका एक ही स्क्रॉल मूल्य (परिदृश्य / पोर्ट्रेट लेआउट आयामों में संभावित अंतर के कारण) नहीं होगा।

ध्यान दें कि यह केवल LinearLayoutManager के साथ काम करता है।

--- सटीक स्क्रॉल को पुनर्स्थापित करने के लिए नीचे, जो संभवतः सूची को अलग दिखने देगा ---

  1. एक OnScrollListener की तरह लागू करें:

    private int mScrollY;
    private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            mScrollY += dy;
        }
    };
    

यह mScrollY में हर समय सटीक स्क्रॉल स्थिति संग्रहीत करेगा।

  1. इस वैरिएबल को अपने बंडल में स्टोर करें, और इसे स्टेट रिस्टोरेशन में एक अलग वेरिएबल में रिस्टोर करें, हम इसे mStateScrollY कहेंगे।

  2. राज्य बहाली के बाद और आपके RecyclerView ने अपने सभी डेटा को रीसेट कर दिया है और इसके साथ स्क्रॉल रीसेट करें:

    mRecyclerView.scrollBy(0, mStateScrollY);

बस।

खबरदार, कि आप स्क्रॉल को एक अलग चर में पुनर्स्थापित करते हैं, यह महत्वपूर्ण है, क्योंकि OnScrollListener को .scrollBy () के साथ बुलाया जाएगा और बाद में mStateSrollY में संग्रहीत मूल्य पर mScrollY सेट करेगा। यदि आप ऐसा नहीं करते हैं तो mScrollY का स्क्रॉल मान दोगुना होगा (क्योंकि OnScrollListener डेल्टास के साथ काम करता है, निरपेक्ष स्क्रॉल नहीं)।

गतिविधियों में राज्य की बचत इस तरह से प्राप्त की जा सकती है:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(ARGS_SCROLL_Y, mScrollY);
}

और अपने onCreate में इसे पुनर्स्थापित करने के लिए ():

if(savedState != null){
    mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0);
}

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


मुझे समझ नहीं आ रहा है कि क्या आप पूर्ण उदाहरण पोस्ट कर सकते हैं?

यह वास्तव में एक पूर्ण उदाहरण के करीब है जैसा कि आप प्राप्त कर सकते हैं यदि आप नहीं चाहते कि मैं अपनी पूरी गतिविधि पोस्ट करूं
A. Steenbergen

मुझे कुछ नहीं मिला। यदि सूची की शुरुआत में आइटम जोड़े गए थे, तो स्क्रॉलिंग की स्थिति में रहना गलत नहीं होगा, क्योंकि आप वास्तव में अब अलग-अलग वस्तुओं को देख रहे होंगे, जो इस क्षेत्र पर ले गए थे?
Android डेवलपर

हां, उस स्थिति में आपको बहाल करते समय स्थिति को समायोजित करना होगा, हालांकि, यह सवाल का हिस्सा नहीं है, जैसा कि आमतौर पर घुमाते समय आप वस्तुओं को जोड़ते या हटाते नहीं हैं। यह अजीब और व्यवहार होगा और शायद उपयोगकर्ता के हित में नहीं, कुछ विशेष मामलों को छोड़कर जो मौजूद हो सकते हैं, जो मैं ईमानदारी से नहीं कर सकता।
ए। स्टीनबर्गेन

9

हां आप एडॉप्टर कंस्ट्रक्टर को केवल एक बार बनाकर इस मुद्दे को हल कर सकते हैं, मैं यहां कोडिंग भाग की व्याख्या कर रहा हूं:

if (appointmentListAdapter == null) {
        appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this);
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener);
        appointmentRecyclerView.setAdapter(appointmentListAdapter);

    } else {
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.notifyDataSetChanged();
    }

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

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

इसलिए मैं बस एडॉप्टर में सूची जोड़ूंगा और नोटिफाइडसेटिंग को कॉल करूंगा।

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

धन्यवाद हैप्पी कोडिंग


मुझे लगता है कि यह स्वीकृत उत्तर @Konrad Morawski
Jithin Jude

5

notifyDataSetChanged()इस तरह लपेटने के लिए @DawnYu उत्तर का उपयोग करके स्क्रॉल स्थिति रखें :

val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() 
adapter.notifyDataSetChanged() 
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)

आदर्श ... सर्वश्रेष्ठ उत्तर
jlandyr

मैं इस जवाब की लंबे समय से तलाश कर रहा हूं। आपका बहुत बहुत धन्यवाद।
अलाआ अबूजरिफा

1

@DawnYu द्वारा शीर्ष उत्तर काम करता है, लेकिन recyclerview पहले शीर्ष पर स्क्रॉल करेगा, फिर वापस इरादा स्क्रॉल स्थिति पर जाएं, जिससे एक "झिलमिलाहट" प्रतिक्रिया हो सकती है जो सुखद नहीं है।

पुनर्नवीनीकरण को फिर से ताज़ा करने के लिए, विशेष रूप से एक और गतिविधि से आने के बाद, बिना चंचलता और स्क्रॉल स्थिति बनाए रखने के लिए, आपको निम्नलिखित करने की आवश्यकता है।

  1. सुनिश्चित करें कि आप DiffUtil का उपयोग करके आपको पुनर्नवीनीकरण दृश्य अपडेट कर रहे हैं। इसके बारे में यहाँ और पढ़ें: https://www.journaldev.com/20873/android-recyclerview-diffutil
  2. अपनी गतिविधि के आधार पर, या उस बिंदु पर जिसे आप अपनी गतिविधि को अपडेट करना चाहते हैं, डेटा को अपने रिसाइक्लेव्यू में लोड करें। DiffUtil का उपयोग करते हुए, केवल पुनरावर्तन के आधार पर अपडेट किया जाएगा जबकि इसे बनाए रखा जाएगा।

उम्मीद है की यह मदद करेगा।


0

मैंने Recyclerview का उपयोग नहीं किया है लेकिन मैंने इसे ListView पर किया है। Recyclerview में नमूना कोड:

setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        rowPos = mLayoutManager.findFirstVisibleItemPosition();

यह श्रोता है जब उपयोगकर्ता स्क्रॉल कर रहा है। ओवरहेड प्रदर्शन महत्वपूर्ण नहीं है। और पहली दिखाई देने वाली स्थिति इस तरह से सटीक है।


ठीक है, findFirstVisibleItemPosition"पहले दृश्यमान दृश्य के एडाप्टर की स्थिति" लौटाता है, लेकिन ऑफसेट के बारे में क्या।
कोनराड मोरावस्की

1
दृश्य स्थिति के लिए सूची दृश्य के सेटिसेलेक्शन विधि के लिए पुनर्नवीनीकरण के लिए एक समान विधि है? यह वही है जो मैंने ListView के लिए किया था।
मूल Android

1
डाई पैरामीटर के साथ सेटस्क्रॉली विधि का उपयोग करने के बारे में कैसे, एक मूल्य जिसे आप बचाएंगे? अगर यह काम करता है, तो मैं अपना जवाब अपडेट करूंगा। मैंने Recyclerview का उपयोग नहीं किया है, लेकिन मैं अपने भविष्य के ऐप में चाहता हूं।
मूल Android

कृपया नीचे स्क्रॉल करें स्थिति को बचाने के बारे में मेरा जवाब देखें।
ए। स्टीनबर्गेन

1
ठीक है, मैं अगली बार इसे ध्यान में रखूंगा, मैंने आपको दुर्भावना के साथ नहीं उतारा। हालाँकि मैं छोटे और अधूरे उत्तरों से असहमत हूँ क्योंकि जब मैंने छोटे उत्तरों की शुरुआत की थी जहाँ अन्य डेवलपर्स ने बहुत सारी पृष्ठभूमि की जानकारी ली थी तो इससे मुझे कोई फायदा नहीं हुआ, क्योंकि वे अक्सर अधूरे होते थे और मुझे बहुत सारे स्पष्ट प्रश्न पूछने पड़ते थे, कुछ आप आमतौर पर पुराने स्टैकओवरफ़्लो पोस्ट पर नहीं कर सकते। यह भी ध्यान दें कि कोनराड द्वारा किसी भी उत्तर को स्वीकार नहीं किया गया था, जो बताता है कि इन उत्तरों से उसकी समस्या का समाधान नहीं हुआ। हालांकि मैं आपका सामान्य बिंदु देखता हूं।
ए। स्टीनबर्गेन

-1
 mMessageAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            mLayoutManager.smoothScrollToPosition(mMessageRecycler, null, mMessageAdapter.getItemCount());
        }
    });

नया संदेश आने पर यहाँ समाधान को पुनर्चक्रण पर स्क्रॉल करते रहना है।

OnChanged () विधि recyclerview पर की गई कार्रवाई का पता लगाती है।


-1

वह मेरे लिए कोटलिन में काम कर रहा है।

  1. एडॉप्टर बनाएं और अपने डेटा को कंस्ट्रक्टर में सौंप दें
class LEDRecyclerAdapter (var currentPole: Pole): RecyclerView.Adapter<RecyclerView.ViewHolder>()  { ... }
  1. इस संपत्ति को बदलें और CallDataSetChanged () को कॉल करें
adapter.currentPole = pole
adapter.notifyDataSetChanged()

स्क्रॉल ऑफ़सेट नहीं बदलता है।


-2

मुझे उन वस्तुओं की सूची के साथ यह समस्या थी जो प्रत्येक को मिनटों में एक समय तक थी जब तक कि वे 'नियत' नहीं थे और अपडेट करने की आवश्यकता थी। मैं डेटा अपडेट करूंगा और फिर कॉल करूंगा

orderAdapter.notifyDataSetChanged();

और यह हर बार शीर्ष पर स्क्रॉल करेगा। मैंने उसके साथ बदल दिया

 for(int i = 0; i < orderArrayList.size(); i++){
                orderAdapter.notifyItemChanged(i);
            }

और यह ठीक था। इस सूत्र में किसी भी अन्य तरीके ने मेरे लिए काम नहीं किया। इस पद्धति का उपयोग करते हुए, इसने प्रत्येक व्यक्तिगत आइटम को फ्लैश किया जब इसे अपडेट किया गया तो मुझे भी इसे माता-पिता के टुकड़े पर रखना पड़ा।

RecyclerView.ItemAnimator animator = orderRecycler.getItemAnimator();
    if (animator instanceof SimpleItemAnimator) {
        ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
    }

-2

यदि आपके पास एक या एक से अधिक EditTexts एक recyclerview आइटम के अंदर हैं, तो इनमें से ऑटोफोकस को अक्षम करें, इस कॉन्फ़िगरेशन को recyclerview के मूल दृश्य में डाल दें :

android:focusable="true"
android:focusableInTouchMode="true"

मेरे पास यह समस्या थी जब मैंने एक अन्य गतिविधि शुरू की थी एक recyclerview आइटम से, जब मैं वापस आया और एक आइटम में एक क्षेत्र का एक अद्यतन सेट किया, जिसमें InformItemChanged (स्थिति) RV चालों का स्क्रॉल था, और मेरा निष्कर्ष था, EditText का ऑटोफोकस आइटम, ऊपर दिए गए कोड ने मेरी समस्या हल कर दी।

श्रेष्ठ।


-3

यदि पुरानी स्थिति और स्थिति समान है तो बस वापस लौटें;

private int oldPosition = -1;

public void notifyItemSetChanged(int position, boolean hasDownloaded) {
    if (oldPosition == position) {
        return;
    }
    oldPosition = position;
    RLog.d(TAG, " notifyItemSetChanged :: " + position);
    DBMessageModel m = mMessages.get(position);
    m.setVideoHasDownloaded(hasDownloaded);
    notifyItemChanged(position, m);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.