2010-01-23 11 views
72

Po uzyskaniu danych dla pojedynczego wiersza z ListView, chcę zaktualizować ten pojedynczy wiersz.ListView Android odśwież pojedynczy wiersz

Obecnie używam notifyDataSetChanged();, ale to sprawia, że ​​View reaguje bardzo powoli. Czy są jakieś inne rozwiązania?

Odpowiedz

29

Jedną opcją jest bezpośrednie sterowanie urządzeniem ListView. Najpierw sprawdź, czy indeks zaktualizowanego wiersza zawiera się między getFirstVisiblePosition() i getLastVisiblePosition(), a te dwie dają pierwsze i ostatnie pozycje w adapterze, które są widoczne na ekranie. Następnie możesz uzyskać wiersz View z getChildAt(int index) i zmienić go.

+3

Dzięki. To działałoby, gdybym ** edytował ** obecny układ dziecka. Ale co jeśli chcę ** całkowicie zmienić układ ** (nadmuchując inny xml)? Jeśli wywołasz 'newView (...)' na adapterze, daje mi to inny widok, który jednak nie jest umieszczony w 'listView'. – hadi

+1

@hadi powinieneś spojrzeć na tę odpowiedź: http://stackoverflow.com/a/11568271/858626 –

6

Poniższy kod pracował dla mnie. Uwaga podczas wywoływania funkcji GetChild() musisz skompensować pierwszy element na liście, ponieważ jest względem niego względny.

int iFirst = getFirstVisiblePosition(); 
int iLast = getLastVisiblePosition(); 

if (indexToChange >= numberOfRowsInSection()) { 
    Log.i("MyApp", "Invalid index. Row Count = " + numberOfRowsInSection()); 
} 
else {  
    if ((index >= iFirst) && (index <= iLast)) { 
     // get the view at the position being updated - need to adjust index based on first in the list 
     View vw = getChildAt(sysvar_index - iFirst); 
     if (null != vw) { 
      // get the text view for the view 
      TextView tv = (TextView) vw.findViewById(com.android.myapp.R.id.scrollingListRowTextView); 
      if (tv != null) { 
       // update the text, invalidation seems to be automatic 
       tv.setText("Item = " + myAppGetItem(index) + ". Index = " + index + ". First = " + iFirst + ". Last = " + iLast); 
      } 
     } 
    } 
} 
87

Romain Guy explained a while back podczas Google I/O session, najbardziej skuteczny sposób, aby aktualizować tylko jeden widok w widoku listy jest coś takiego jak poniżej (ten zaktualizować całe dane view):

ListView list = getListView(); 
int start = list.getFirstVisiblePosition(); 
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++) 
    if(target==list.getItemAtPosition(i)){ 
     View view = list.getChildAt(i-start); 
     list.getAdapter().getView(i, view, list); 
     break; 
    } 

Zakładając, że target jest jedną z pozycji adaptera.

Ten kod pobrać ListView, a następnie przeglądać aktualnie pokazane widoki, porównanie target przedmiot szukasz ze sobą wyświetlane Zerknij jeszcze na pozycje, a jeśli cel jest wśród tych, uzyskać widok otaczającej i wykonać zasilacz getView na tym widok, aby odświeżyć wyświetlacz.

marginesie invalidate nie działa jak niektórzy ludzie oczekują i nie będzie odświeżać widok jak getView nie, notifyDataSetChanged będzie odbudować całą listę i kończy się wywołaniem getview dla każdego wyświetlanych pozycji i invalidateViews wpłynie także grono.

Jedną z ostatnich rzeczy jest to, że można uzyskać dodatkową wydajność, jeśli wystarczy zmienić widok dziecka z rzędu, a nie cały wiersz, taki jak getView. W takim przypadku, następujący kod może zastąpić list.getAdapter().getView(i, view, list); (przykład zmienić TextView tekst):

((TextView)view.findViewById(R.id.myid)).setText("some new text"); 

w kodzie ufamy.

+15

Powinieneś po prostu połączyć swoją drugą odpowiedź http://stackoverflow.com/a/9987616/1026132 zamiast kopiowania i wklejenie tutaj. Pomogłoby to zmniejszyć ilość zduplikowanych treści w SO. –

+2

jak zaimplementować to z innym typem widoku? – Zyoo

+1

Link do "sesji Google I/O" w odpowiedzi jest martwy. – Pang

26

Ta prostsza metoda działa dobrze dla mnie, i trzeba tylko wiedzieć, indeks stanie uzyskać Ahold widoku:

// mListView is an instance variable 

private void updateItemAtPosition(int position) { 
    int visiblePosition = mListView.getFirstVisiblePosition(); 
    View view = mListView.getChildAt(position - visiblePosition); 
    mListView.getAdapter().getView(position, view, mListView); 
} 
+2

Działa jak zaklęcie, +1 – Rutger

+0

wyślę zaktualizowane wartości, aby wyświetlić szczegóły innej strony, jeśli użytkownik zmieni na tej stronie szczegółów, jak zaktualizować z powrotem do listy, proszę – Harsha

2

Jest jeszcze inna o wiele bardziej efektywne, co można zrobić, jeśli pasuje do Twojego przypadek użycia.

Jeśli zmieniasz stan i możesz w jakiś sposób wywołać właściwe (znając pozycję) mListView.getAdapter().getView() będzie to najbardziej efektywny ze wszystkich.

Potrafię przedstawić bardzo prosty sposób, tworząc anonimową klasę wewnętrzną w klasie ListAdapter.getView().W tym przykładzie mam TextView przedstawiający tekst „nowe” i że widok jest ustawiony na GONE gdy element listy kliknięciu:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    // assign the view we are converting to a local variable 
    View view = convertView; 

    Object quotation = getItem(position); 
    // first check to see if the view is null. if so, we have to inflate it. 
    if (view == null) 
     view = mInflater.inflate(R.layout.list_item_quotation, parent, false); 

    final TextView newTextView = (TextView) view.findViewById(R.id.newTextView); 

    view.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      if (mCallbacks != null) 
       mCallbacks.onItemSelected(quotation.id); 
      if (!quotation.isRead()) { 
       servicesSingleton.setQuotationStatusReadRequest(quotation.id); 
       quotation.setStatusRead(); 
       newTextView.setVisibility(View.GONE); 
      } 
     } 
    }); 

    if(quotation.isRead()) 
     newTextView.setVisibility(View.GONE); 
    else 
     newTextView.setVisibility(View.VISIBLE); 

    return view; 
} 

Ramy automatycznie używa poprawnej position i nie trzeba się martwić o ściągam go ponownie przed wywołaniem getView.

-1

Czy ktoś podał do wiadomości "Widok widoku" w metodzie zastąpienia?

public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 

        //Update the selected item 
        ((TextView)view.findViewById(R.id.cardText2)).setText("done!"); 

       } 
+0

Pytanie brzmiało, jak odświeżyć pojedynczy wiersz w ListView w dowolnym danym za chwilę. onItemClick jest wywoływany tylko wtedy, gdy użytkownik kliknie element, a więc nie jest wystarczający – Thys