मैं सूची दृश्य में एकल पंक्ति कैसे अपडेट कर सकता हूं?


131

मेरे पास ListViewसमाचार आइटम प्रदर्शित करता है। उनमें एक छवि, एक शीर्षक और कुछ पाठ शामिल हैं। छवि को एक अलग थ्रेड (एक कतार और सभी के साथ) में लोड किया गया है और जब छवि डाउनलोड की जाती है, तो मैं अब notifyDataSetChanged()छवि को अपडेट करने के लिए सूची एडाप्टर पर कॉल करता हूं । यह काम करता है, लेकिन सभी दृश्य वस्तुओं के लिए कॉल करने के getView()बाद से बहुत बार notifyDataSetChanged()कॉल getView()किया जा रहा है। मैं सूची में सिर्फ एक आइटम को अपडेट करना चाहता हूं। यह मैं कैसे करूंगा?

मेरे वर्तमान दृष्टिकोण से मुझे जो समस्याएँ हैं:

  1. स्क्रॉलिंग धीमी है
  2. मेरे पास छवि पर एक फीका-एनीमेशन है जो हर बार सूची में एक नई छवि लोड होने पर होता है।

जवाबों:


199

मुझे जवाब मिला, आपकी जानकारी के लिए धन्यवाद मिशेल। आप वास्तव में उपयोग करके सही दृश्य प्राप्त कर सकते हैं View#getChildAt(int index)। पकड़ यह है कि यह पहली दिखाई देने वाली वस्तु से गिनना शुरू करता है। वास्तव में, आप केवल दृश्यमान आइटम प्राप्त कर सकते हैं। आप इससे हल करें ListView#getFirstVisiblePosition()

उदाहरण:

private void updateView(int index){
    View v = yourListView.getChildAt(index - 
        yourListView.getFirstVisiblePosition());

    if(v == null)
       return;

    TextView someText = (TextView) v.findViewById(R.id.sometextview);
    someText.setText("Hi! I updated you manually!");
}

2
स्वयं पर ध्यान दें: mImgView.setImageURI () सूची आइटम को केवल एक बार अपडेट करेगा; इसलिए मैं इसके बजाय mLstVwAdapter.notifyDataSetInvalidated () का उपयोग करके बेहतर हूं।
kellogs

यह समाधान काम करता है। लेकिन यह केवल आपकेListView.getChildAt (इंडेक्स) के साथ किया जा सकता है; और दृश्य बदल सकते हैं। दोनों समाधान काम !!
AndroidDev

क्या होगा यदि मैं एडेप्टर पर सिर्फ getView () को कॉल करता हूं यदि स्थिति getFirstVoublePosition () और getLastVoublePosition () के बीच है? यह एक ही काम करेगा? मुझे लगता है कि यह सामान्य रूप से दृश्य को अपडेट करेगा, है ना?
एंड्रॉयड डेवलपर

मेरे मामले में, कुछ समय का दृश्य अशक्त है, क्योंकि प्रत्येक आइटम के असिंक्रोनस रूप से अपडेट होने पर, अगर देखने के लिए बेहतर जांच हो! = Null
Khawar

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

71

यह प्रश्न Google I / O 2010 में पूछा गया है, आप इसे यहाँ देख सकते हैं:

ListView की दुनिया, समय 52:30

मूल रूप से रोमेन गाई बताते हैं कि दृश्य को प्राप्त करने के लिए कॉल getChildAt(int)करना है ListViewऔर (मुझे लगता है) getFirstVisiblePosition()स्थिति और सूचकांक के बीच संबंध का पता लगाने के लिए कॉल करें ।

रोमैन भी एक उदाहरण के रूप में अलमारियों नामक परियोजना की ओर इशारा करता है, मुझे लगता है कि वह विधि का मतलब हो सकता है ShelvesActivity.updateBookCovers(), लेकिन मुझे कॉल नहीं मिल सकता है getFirstVisiblePosition()

भयानक अद्यतन आ रहा है:

निकट भविष्य में RecyclerView इसे ठीक कर देगा। जैसा कि http://www.grokkingandroid.com/first-glance-androids-recyclerview/ पर बताया गया है , आप बदलावों को सटीक रूप से निर्दिष्ट करने के लिए तरीकों को कॉल करने में सक्षम होंगे, जैसे:

void notifyItemInserted(int position)
void notifyItemRemoved(int position)
void notifyItemChanged(int position)

इसके अलावा, हर कोई RecyclerView के आधार पर नए विचारों का उपयोग करना चाहेगा क्योंकि उन्हें अच्छी तरह से दिखने वाले एनिमेशन से पुरस्कृत किया जाएगा! भविष्य भयानक लग रहा है! :-)


3
अची बात है! :) हो सकता है कि आप यहाँ एक अशक्त जाँच जोड़ सकते हैं, अगर दृश्य क्षण में उपलब्ध नहीं है, तो v भी शून्य हो सकता है। और यदि उपयोगकर्ता ListView को स्क्रॉल करता है, तो निश्चित रूप से यह डेटा चला जाएगा, इसलिए किसी को भी एडेप्टर में डेटा अपडेट करना चाहिए (शायद बिना इनफॉर्मेटडेटसेटचेनडेज ())। सामान्य तौर पर मुझे लगता है कि एडॉप्टर के भीतर यह सब तर्क रखने के लिए एक अच्छा विचार होगा, अर्थात इसके लिए सूची दृश्य संदर्भ पास करना।
मैरिकेल्ट सेप

यह मेरे लिए काम करता है, सिवाय - जब दृश्य स्क्रीन से बाहर निकलता है, जब यह परिवर्तन फिर से प्रवेश करता है तो रीसेट होता है। मुझे लगता है क्योंकि गेटव्यू में यह अभी भी मूल जानकारी प्राप्त कर रहा है। मैं इसके आसपास कैसे पहुंचू?
ग्लोसकेरीबॉम्ब

6

मैंने इस तरह से इसे किया:

आपके आइटम (पंक्तियों) में विशिष्ट आईडी होनी चाहिए ताकि आप उन्हें बाद में अपडेट कर सकें। हर दृश्य का टैग सेट करें जब सूची को एडॉप्टर से दृश्य मिल रहा है। (यदि आप डिफ़ॉल्ट टैग का उपयोग कहीं और किया जाता है तो आप कुंजी टैग का भी उपयोग कर सकते हैं)

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    View view = super.getView(position, convertView, parent);
    view.setTag(getItemId(position));
    return view;
}

अद्यतन के लिए सूची के प्रत्येक तत्व की जाँच करें, यदि दिए गए आईडी के साथ कोई दृश्य दिखाई देता है तो हम अद्यतन करते हैं।

private void update(long id)
{

    int c = list.getChildCount();
    for (int i = 0; i < c; i++)
    {
        View view = list.getChildAt(i);
        if ((Long)view.getTag() == id)
        {
            // update view
        }
    }
}

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


3

इस मॉडल क्लास ऑब्जेक्ट की तरह मॉडल वर्ग को पहले वैश्विक मानें

SampleModel golbalmodel=new SchedulerModel();

और इसे वैश्विक रूप से शुरू किया

इसे मॉडल द्वारा वैश्विक मॉडल से आरंभ करके दृश्य की वर्तमान पंक्ति प्राप्त करें

SampleModel data = (SchedulerModel) sampleList.get(position);
                golbalmodel=data;

परिवर्तित किए गए मान को वैश्विक मॉडल ऑब्जेक्ट विधि पर सेट करने के लिए सेट करें और मेरे लिए अपने काम को सूचित करें

golbalmodel.setStartandenddate(changedate);

notifyDataSetChanged();

यहाँ इस पर संबंधित प्रश्न अच्छे उत्तरों के साथ है।


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

2

उत्तर स्पष्ट और सही हैं, मैं CursorAdapterयहां मामले के लिए एक विचार जोड़ूंगा।

यदि आप उप-वर्गिंग CursorAdapter(या ResourceCursorAdapter, या SimpleCursorAdapter) करते हैं, तो आप ViewBinderया तो कार्यान्वित या ओवरराइड bindView()और newView()विधियां प्राप्त करते हैं, ये तर्कों में वर्तमान सूची आइटम इंडेक्स प्राप्त नहीं करते हैं। इसलिए, जब कुछ डेटा आता है और आप प्रासंगिक दृश्य सूची आइटम को अपडेट करना चाहते हैं, तो आप उनके सूचकांकों को कैसे जानते हैं?

मेरा समाधान यह था:

  • सभी बनाई गई सूची आइटम विचारों की एक सूची रखें, इस सूची से आइटम जोड़ें newView()
  • जब डेटा आता है, तो उन्हें पुनरावृत्त करें और देखें कि अपडेट करने के लिए किसकी ज़रूरत है - notifyDatasetChanged()उन सभी को करने और ताज़ा करने से बेहतर

व्यू रेफरेंस की संख्या को देखने के कारण मुझे स्टोर करने की आवश्यकता होगी और स्क्रीन पर दिखाई देने वाली सूची आइटम की संख्या के बराबर होगा।


2
int wantedPosition = 25; // Whatever position you're looking for
int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;

if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) {
    Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
    return;
}

View wantedView = linearLayoutManager.getChildAt(wantedChild);
mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over);
mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup);

mlayoutOver.setVisibility(View.INVISIBLE);
mlayoutPopup.setVisibility(View.VISIBLE);

RecycleView के लिए कृपया इस कोड का उपयोग करें


मैंने इसे recycleView पर लागू किया, यह काम करता है। लेकिन जब आप सूची को स्क्रॉल करते हैं तो यह फिर से बदल जाता है। कोई मदद?
फहीद नदीम

1

मैंने उस कोड का उपयोग किया जो एरिक प्रदान करता है, महान काम करता है, लेकिन मेरे पास मेरी सूची के लिए एक जटिल कस्टम एडेप्टर है और मैं यूआई को अपडेट करने वाले कोड के दो बार कार्यान्वयन के साथ सामना किया गया था। मैंने अपने एडेप्टर getView पद्धति से नया दृश्य प्राप्त करने की कोशिश की है (सूची डेटा रखने वाली सरणी सूची पहले से ही अपडेट या बदल दी गई है:

View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition());
if(cell!=null){
    cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent)
    cell.startAnimation(animationLeftIn());
}

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


1

वास्तव में मैंने यह प्रयोग किया है

private void updateSetTopState(int index) {
        View v = listview.getChildAt(index -
                listview.getFirstVisiblePosition()+listview.getHeaderViewsCount());

        if(v == null)
            return;

        TextView aa = (TextView) v.findViewById(R.id.aa);
        aa.setVisibility(View.VISIBLE);
    }

मुझे सापेक्ष लेआउट के रूप में दृश्य मिल रहा है कि रिश्तेदार लेआउट के अंदर टेक्स्टव्यू कैसे खोजें?
गिरीश

1

मैंने एक और समाधान बनाया, जैसे कि RecyclyerView विधि शून्य notifyItemChanged(int position), इस तरह से CustomBaseAdapter वर्ग बनाएं :

public abstract class CustomBaseAdapter implements ListAdapter, SpinnerAdapter {
    private final CustomDataSetObservable mDataSetObservable = new CustomDataSetObservable();

    public boolean hasStableIds() {
        return false;
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

    public void notifyItemChanged(int position) {
        mDataSetObservable.notifyItemChanged(position);
    }

    public void notifyDataSetInvalidated() {
        mDataSetObservable.notifyInvalidated();
    }

    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEnabled(int position) {
        return true;
    }

    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }

    public int getItemViewType(int position) {
        return 0;
    }

    public int getViewTypeCount() {
        return 1;
    }

    public boolean isEmpty() {
        return getCount() == 0;
    } {

    }
}

इस तरह से CustomAdapterClass में चर के लिए भी एक CustomDataSetObservable वर्ग बनाने के लिए मत भूलना :mDataSetObservable

public class CustomDataSetObservable extends Observable<DataSetObserver> {

    public void notifyChanged() {
        synchronized(mObservers) {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }

    public void notifyItemChanged(int position) {
        synchronized(mObservers) {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            mObservers.get(position).onChanged();
        }
    }
}

क्लास में CustomBaseAdapter की एक विधि है notifyItemChanged(int position), और आप जब चाहें (बटन क्लिक या कहीं भी आप उस विधि को कॉल करना चाहते हैं) से एक पंक्ति को अपडेट करने के लिए उस विधि को कॉल कर सकते हैं। और वोइला !, आपकी एकल पंक्ति तुरन्त अपडेट होगी।


0

मेरा समाधान: यदि यह सही है *, पूरी सूची को पुन: आरेखित किए बिना डेटा और देखने योग्य वस्तुओं को अपडेट करें। अन्य सूचना

सही - पुराना डेटा आकार == नया डेटा आकार, और पुराना डेटा आईडी और उनका क्रम == नया डेटा आईडी और ऑर्डर

किस तरह:

/**
 * A View can only be used (visible) once. This class creates a map from int (position) to view, where the mapping
 * is one-to-one and on.
 * 
 */
    private static class UniqueValueSparseArray extends SparseArray<View> {
    private final HashMap<View,Integer> m_valueToKey = new HashMap<View,Integer>();

    @Override
    public void put(int key, View value) {
        final Integer previousKey = m_valueToKey.put(value,key);
        if(null != previousKey) {
            remove(previousKey);//re-mapping
        }
        super.put(key, value);
    }
}

@Override
public void setData(final List<? extends DBObject> data) {
    // TODO Implement 'smarter' logic, for replacing just part of the data?
    if (data == m_data) return;
    List<? extends DBObject> oldData = m_data;
    m_data = null == data ? Collections.EMPTY_LIST : data;
    if (!updateExistingViews(oldData, data)) notifyDataSetChanged();
    else if (DEBUG) Log.d(TAG, "Updated without notifyDataSetChanged");
}


/**
 * See if we can update the data within existing layout, without re-drawing the list.
 * @param oldData
 * @param newData
 * @return
 */
private boolean updateExistingViews(List<? extends DBObject> oldData, List<? extends DBObject> newData) {
    /**
     * Iterate over new data, compare to old. If IDs out of sync, stop and return false. Else - update visible
     * items.
     */
    final int oldDataSize = oldData.size();
    if (oldDataSize != newData.size()) return false;
    DBObject newObj;

    int nVisibleViews = m_visibleViews.size();
    if(nVisibleViews == 0) return false;

    for (int position = 0; nVisibleViews > 0 && position < oldDataSize; position++) {
        newObj = newData.get(position);
        if (oldData.get(position).getId() != newObj.getId()) return false;
        // iterate over visible objects and see if this ID is there.
        final View view = m_visibleViews.get(position);
        if (null != view) {
            // this position has a visible view, let's update it!
            bindView(position, view, false);
            nVisibleViews--;
        }
    }

    return true;
}

और निश्चित रूप से:

@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
    final View result = createViewFromResource(position, convertView, parent);
    m_visibleViews.put(position, result);

    return result;
}

BindView के लिए अंतिम पैराम को अनदेखा करें (मैं इसका उपयोग यह निर्धारित करने के लिए करता हूं कि मुझे इमेजडबल के लिए बिटमैप रीसायकल करने की आवश्यकता है या नहीं)।

जैसा कि ऊपर उल्लेख किया गया है, 'दृश्यमान' विचारों की कुल संख्या लगभग वह राशि है जो स्क्रीन पर फिट होती है (अभिविन्यास परिवर्तनों को अनदेखा करते हुए), इसलिए कोई बिग्गी मेमोरी-वार नहीं।


0

इस समाधान के अतिरिक्त ( https://stackoverflow.com/a/3727813/5218712 ) केवल यह जोड़ना चाहते हैं कि यह तभी काम करेगा जबlistView.getChildCount() == yourDataList.size(); लिस्ट व्यू के अंदर अतिरिक्त दृश्य होने पर ।

बाल तत्व कैसे आबाद होते हैं, इसका उदाहरण:  listView.m.Children array

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