2010-01-23 15 views
72

Dopo aver ottenuto i dati per una singola riga di un ListView, voglio aggiornare quella singola riga.Elenco Android Aggiorna riga singola

Attualmente sto usando notifyDataSetChanged(); ma questo rende lo View molto lento. ci sono altre soluzioni?

risposta

29

Un'opzione è di manipolare direttamente lo ListView. Innanzitutto controlla se l'indice della riga aggiornata è tra getFirstVisiblePosition() e getLastVisiblePosition(), questi due ti danno la prima e l'ultima posizione nell'adattatore che sono visibili sullo schermo. Quindi è possibile ottenere la riga View con getChildAt(int index) e modificarla.

+3

Grazie. Questo funzionerebbe se sto ** modificando ** il layout corrente del bambino. Ma cosa succede se voglio ** cambiare il layout del tutto ** (gonfiando un diverso xml)? Se chiamo 'newView (...)' sull'adattatore, mi dà una vista diversa che tuttavia non è inserita in 'listView'. – hadi

+1

@hadi dovresti dare un'occhiata a questa risposta: http://stackoverflow.com/a/11568271/858626 –

6

Il codice seguente ha funzionato per me. Nota quando chiami GetChild() devi compensare dal primo elemento nella lista dal suo relativo a quello.

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

Come Romain Guy explained a while back durante la Google I/O session, il modo più efficiente per aggiornare solo una vista in una vista elenco è qualcosa di simile alla seguente (questo uno aggiornare l'intero dati della vista):

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

Supponendo che target sia un elemento dell'adattatore.

Questo codice recuperare la ListView, quindi visualizza le viste attualmente indicati, confrontare la voce target siete alla ricerca di uno con l'visualizzate le voci vista, e se il vostro obiettivo è tra quelli, ottenere la vista che racchiude ed eseguire l'adattatore getView su quel visualizza per aggiornare il display.

Come nota a margine invalidate non funziona come alcune persone si aspettano e non saranno aggiornare la vista come GetView fa, notifyDataSetChanged sarà ricostruire l'intera lista e finiscono per chiamare getview ogni elementi visualizzati e invalidateViews influirà anche un mazzo.

Un'ultima cosa, si può anche ottenere prestazioni extra se ha solo bisogno di cambiare una vista figlio di una riga e non l'intera riga come getView. In tal caso, il seguente codice può sostituire list.getAdapter().getView(i, view, list); (esempio per cambiare un testo TextView):

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

Nel codice ci fidiamo.

+15

Probabilmente dovresti semplicemente collegare l'altra risposta http://stackoverflow.com/a/9987616/1026132 invece di copiare e incollare qui. Ciò contribuirebbe a ridurre la quantità di contenuti duplicati in SO. –

+2

come implementarlo con un tipo di visualizzazione diverso? – Zyoo

+1

Il collegamento a "sessione I/O Google" in risposta è morto. – Pang

26

Questo metodo più semplice funziona bene per me, e hai solo bisogno di conoscere l'indice di posizione per ottenere ahold della vista:

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

Funziona come un incantesimo, +1 – Rutger

+0

invierò i valori aggiornati per visualizzare in dettaglio un'altra pagina se l'utente apporta modifiche alla pagina dei dettagli come aggiornare di nuovo all'elenco – Harsha

2

C'è un'altra cosa molto più efficiente che si può fare, se si adatta il vostro caso d'uso che è.

Se si modifica lo stato e si può in qualche modo chiamare il corretto (conoscendo la posizione) mListView.getAdapter().getView() sarà il più efficiente di tutti.

Posso dimostrare un modo molto semplice per farlo, creando una classe interna anonima nella mia classe ListAdapter.getView().In questo esempio ho un TextView che mostra un testo "nuovo" e che visualizzazione è impostata su GONE quando la voce di elenco si fa clic:

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

Il quadro utilizza automaticamente il corretto position E non devi preoccuparti di averlo scaricato di nuovo prima di chiamare getView.

-1

Solo per la cronaca, qualcuno ha considerato la "vista di visualizzazione" sul metodo di sostituzione?

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

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

       } 
+0

La domanda era come aggiornare una singola riga in ListView in qualsiasi dato momento. onItemClick viene chiamato solo quando gli utenti fanno clic su un elemento e quindi non è sufficiente – Thys