notifyDataSetChange non funziona dall'adattatore personalizzato


126

Quando ripopolo il mio ListView, chiamo un metodo specifico dal mio Adapter.

Problema :

Quando chiamo updateReceiptsListda mio Adapter, i dati vengono aggiornati, ma il mio ListViewnon riflette la modifica.

Domanda :

Perché non ListViewmostro i nuovi dati quando chiamo notifyDataSetChanged?

Adattatore :

public class ReceiptListAdapter extends BaseAdapter {

    public List<Receipt> receiptlist;
    private Context context;
    private LayoutInflater inflater;
    private DateHelpers dateH;

    public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
        context = mcontext;
        receiptlist = rl;
        Collections.reverse(receiptlist);
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        dateH = new DateHelpers();
    }

    @Override
    public int getCount() {
        try {
            int size = receiptlist.size();
            return size;
        } catch(NullPointerException ex) {
            return 0;
        }
    }

    public void updateReceiptsList(List<Receipt> newlist) {
        receiptlist = newlist;
        this.notifyDataSetChanged();
    }

    @Override
    public Receipt getItem(int i) {
        return receiptlist.get(i);
    }

    @Override
    public long getItemId(int i) {
        return receiptlist.get(i).getReceiptId() ;
    }

    private String getPuntenString(Receipt r) {
        if(r.getPoints().equals("1")) {
            return "1 punt";
        }
        return r.getPoints()+" punten";
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;

        final Receipt receipt = receiptlist.get(position);
        ReceiptViewHolder receiptviewholder;
        Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");        
        Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");

        if (vi == null) { //convertview==null
            receiptviewholder = new ReceiptViewHolder();
            vi = inflater.inflate(R.layout.view_listitem_receipt, null);
            vi.setOnClickListener(null);
            vi.setOnLongClickListener(null);
            vi.setLongClickable(false);
            receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
            receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
            receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
            receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
            receiptviewholder.shop.setTypeface(tf_hn_bold);
            receiptviewholder.price.setTypeface(tf_hn_bold);
            vi.setTag(receiptviewholder);
        }else{//convertview is not null
            receiptviewholder = (ReceiptViewHolder)vi.getTag();
        }

        receiptviewholder.shop.setText(receipt.getShop());
        receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
        receiptviewholder.price.setText("€ "+receipt.getPrice());
        receiptviewholder.points.setText(getPuntenString(receipt));

        vi.setClickable(false);
        return vi;
    }

    public static class ReceiptViewHolder {
        public TextView shop;
        public TextView date;
        public TextView price;
        public TextView points;
    }

    public Object getFilter() {
        // XXX Auto-generated method stub
        return null;
    }

}

--MODIFICARE:

trovato soluzione alternativa

Solo per avere un codice funzionale che faccio ora:

listview.setAdapter( new ReceiptListAdapter(activity,mcontext, -new dataset-);

Funziona, ma non come dovrebbe funzionare.


stackoverflow.com/a/4198569/2382964 , Ciao Jasper, fai riferimento a questo link ... questo ti aiuterà.
Tushar Pandey

prova altri metodi come notifyItemInserted, notifyItemRemoved, ecc.
Reejesh PK

Risposte:


334

Cambia il tuo metodo da

public void updateReceiptsList(List<Receipt> newlist) {
    receiptlist = newlist;
    this.notifyDataSetChanged();
}

Per

public void updateReceiptsList(List<Receipt> newlist) {
    receiptlist.clear();
    receiptlist.addAll(newlist);
    this.notifyDataSetChanged();
}

Quindi mantieni lo stesso oggetto del tuo DataSet nel tuo adattatore.


considera di avere un oggetto genitore come ReceiptListObjectinvece di un Listoggetto, cosa puoi fare allora per risolvere questo problema?
prom85

@ prom85 gli ArrayAdapters possono associarsi anche a oggetti diversi da elenchi o array? Non lo sapevo.
tolgap

si tratta di un BaseAdaptere questo adattatore non sa a quali dati è associato ... quindi se ho un oggetto personalizzato e utilizzo le funzioni personalizzate di questo oggetto (come custObject.getCount()e custObject.getChildAt(int i)per esempio) e voglio scambiare questo oggetto, notifyDataSetChangednon funziona ... comunque, penso che questo problema non si verifichi mai con unArrayAdapter
prom85

3
Puoi forse spiegare perché il primo metodo non funziona e il secondo funziona con un BaseAdapter?
Tooroop

1
commenti come Grazie o +1 non sono ammessi ma su alcune risposte mi piace molto ringraziare :)
Muhammad Saqib

24

Ho lo stesso problema e me ne rendo conto. Quando creiamo l'adattatore e lo impostiamo su listview, listview punterà all'oggetto da qualche parte nella memoria che l'adattatore contiene, i dati in questo oggetto verranno visualizzati in listview.

adapter = new CustomAdapter(data);
listview.setadapter(adapter);

se creiamo di nuovo un oggetto per l'adattatore con un altro dato e notifydatasetchanged ():

adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();

ciò non influirà sui dati in listview perché l'elenco punta a un oggetto diverso, questo oggetto non conosce nulla del nuovo oggetto nell'adattatore e notifyDataSetChanged () non influisce su nulla. Quindi dovremmo modificare i dati in oggetto ed evitare di creare di nuovo un nuovo oggetto per l'adattatore


10
stai ricreando l'oggetto adattatore, non è efficiente
Alezis

2
@ Alezis Penso che sia esattamente ciò che intendeva Nhan.
Neeraj Sewani

17

Come ho già spiegato le ragioni alla base di questo problema e anche come gestirlo in un thread di risposta diverso qui . Sto ancora condividendo il riepilogo della soluzione qui.

Uno dei motivi principali per notifyDataSetChanged()cui non funzionerà è,

Il tuo adattatore perde il riferimento al tuo elenco .

Quando si crea e si aggiunge un nuovo elenco al file Adapter. Segui sempre queste linee guida:

  1. Inizializza il arrayListwhile dichiarandolo globalmente.
  2. Aggiungere l'elenco all'adattatore direttamente senza controllare i valori nulli e vuoti. Imposta l'adattatore direttamente nell'elenco (non controllare alcuna condizione). Adapter ti garantisce che ovunque apporterai modifiche ai dati del te arrayListne prenderà cura, ma non perderà mai il riferimento.
  3. Modifica sempre i dati nell'arrayList stesso (se i tuoi dati sono completamente nuovi rispetto a quelli che puoi chiamare adapter.clear()e arrayList.clear()prima di aggiungere effettivamente i dati all'elenco) ma non impostare l'adattatore, ad esempio se i nuovi dati sono popolati nel campo arrayListdi adapter.notifyDataSetChanged()

Spero che questo ti aiuti.


1
Grazie! La mancia al n. 2 ha aiutato.
Cheruby

4

Forse prova ad aggiornare il tuo ListView:

receiptsListView.invalidate().

EDIT: Un altro pensiero mi è venuto in mente. Per la cronaca, prova a disabilitare la cache della visualizzazione elenco:

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />

Strano, la mia ultima idea è riassegnare l'adattatore alla visualizzazione elenco dopo la modifica dei dati. Ma suppongo che tu abbia già provato anche questo.
Rafal Gałka

3

Ho avuto lo stesso problema usando ListAdapter

Ho lasciato che Android Studio implementasse i metodi per me e questo è quello che ho ottenuto:

public class CustomAdapter implements ListAdapter {
    ...
    @Override
    public void registerDataSetObserver(DataSetObserver observer) {

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Il problema è che questi metodi non chiamano superimplementazioni, quindi notifyDataSetChangenon vengono mai chiamati.

Rimuovi queste sostituzioni manualmente o aggiungi super chiamate e dovrebbe funzionare di nuovo.

@Override
public void registerDataSetObserver(DataSetObserver observer) {
    super.registerDataSetObserver(observer);
}

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    super.unregisterDataSetObserver(observer);
}

2
class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

    private void capNhatDuLieu(ArrayList<LichHocDTO> list){
        this.studentList.clear();
        this.studentList.addAll(list);
        this.notifyDataSetChanged();
    }
}

Puoi provare. Per me funziona



1

Se per caso sei arrivato su questo thread e ti chiedi perché adapter.invaidate()o adapter.clear()metodi non sono presenti nel tuo caso, allora forse perché potresti utilizzare al RecyclerView.Adapterposto di quello BaseAdapterche viene utilizzato dal richiedente di questa domanda. Se si cancella listo arraylistnon si risolve il problema, è possibile che tu stia creando due o più istanze del adapterper es .:

Attività principale

...

adapter = new CustomAdapter(list);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);

...

e
SomeFragment

...

adapter = new CustomAdapter(newList);
adapter.notifyDataSetChanged();

...

Se nel secondo caso ti aspetti un cambiamento nell'elenco delle viste gonfiate nella vista riciclatore, allora non accadrà poiché nella seconda volta adapterviene creata una nuova istanza di che non è allegata alla vista riciclatrice. L'impostazione notifyDataSetChangednel secondo adattatore non cambierà il contenuto della visualizzazione riciclabile. A tale scopo, creare una nuova istanza della vista riciclatore in SomeFragment e collegarla alla nuova istanza dell'adattatore.

SomeFragment

...

recyclerView = new RecyclerView();
adapter = new CustomAdapter();
recyclerView.setAdapter(adapter);

...

Tuttavia, non consiglio di creare più istanze dello stesso adattatore e visualizzazione riciclatore.


0

Aggiungi questo codice

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });

0

Ho lo stesso problema ma l'ho appena finito !!

dovresti cambiare in

public class ReceiptListAdapter extends BaseAdapter {

    public List<Receipt> receiptlist;
    private Context context;
    private LayoutInflater inflater;
    private DateHelpers dateH;
    private List<ReceiptViewHolder> receiptviewlist;

    public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
        context = mcontext;
        receiptlist = rl;
        receiptviewlist = new ArrayList<>();
        receiptviewlist.clear();
        for(int i = 0; i < receiptlist.size(); i++){
          ReceiptViewHolder receiptviewholder = new ReceiptViewHolder();
          receiptviewlist.add(receiptviewholder);
        }
        Collections.reverse(receiptlist);
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        dateH = new DateHelpers();
    }

    @Override
    public int getCount() {
        try {
            int size = receiptlist.size();
            return size;
        } catch(NullPointerException ex) {
            return 0;
        }
    }

    public void updateReceiptsList(List<Receipt> newlist) {
        receiptlist = newlist;
        this.notifyDataSetChanged();
    }

    @Override
    public Receipt getItem(int i) {
        return receiptlist.get(i);
    }

    @Override
    public long getItemId(int i) {
        return receiptlist.get(i).getReceiptId() ;
    }

    private String getPuntenString(Receipt r) {
        if(r.getPoints().equals("1")) {
            return "1 punt";
        }
        return r.getPoints()+" punten";
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;

        final Receipt receipt = receiptlist.get(position);
        ReceiptViewHolder receiptviewholder;
        Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");        
        Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");

        if (vi == null) { //convertview==null
            ReceiptViewHolder receiptviewholder = receiptviewlist.get(position);
            vi = inflater.inflate(R.layout.view_listitem_receipt, null);
            vi.setOnClickListener(null);
            vi.setOnLongClickListener(null);
            vi.setLongClickable(false);
            receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
            receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
            receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
            receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
            receiptviewholder.shop.setTypeface(tf_hn_bold);
            receiptviewholder.price.setTypeface(tf_hn_bold);
            vi.setTag(receiptviewholder);
        }else{//convertview is not null
            receiptviewholder = (ReceiptViewHolder)vi.getTag();
        }

        receiptviewholder.shop.setText(receipt.getShop());
        receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
        receiptviewholder.price.setText("€ "+receipt.getPrice());
        receiptviewholder.points.setText(getPuntenString(receipt));

        vi.setClickable(false);
        return vi;
    }

    public static class ReceiptViewHolder {
        public TextView shop;
        public TextView date;
        public TextView price;
        public TextView points;
    }

    public Object getFilter() {
        // XXX Auto-generated method stub
        return null;
    }

}

0

Il mio caso era diverso ma potrebbe essere lo stesso per altri

per coloro che ancora non sono riusciti a trovare una soluzione e hanno provato tutto quanto sopra, se stai usando l'adattatore all'interno del frammento, il motivo per cui non funziona fragment could be recreating so the adapter is recreating everytime the fragment recreate

è necessario verificare se l'adattatore e l'elenco degli oggetti sono nulli prima dell'inizializzazione

if(adapter == null){
  adapter = new CustomListAdapter(...);
}
...

if(objects == null){
  objects = new ArrayList<>();
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.