notifyDataSetChange funktioniert nicht über einen benutzerdefinierten Adapter


125

Wenn ich meine wieder auffülle ListView, rufe ich eine bestimmte Methode von meiner auf Adapter.

Problem :

Wenn ich updateReceiptsListvon meinem aus anrufe Adapter, werden die Daten aktualisiert, aber mein ListViewspiegelt die Änderung nicht wider.

Frage :

Warum werden meine ListViewneuen Daten nicht angezeigt , wenn ich anrufe notifyDataSetChanged?

Adapter :

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

}

--BEARBEITEN:

Workaround gefunden

Nur um einen Funktionscode zu haben, den ich jetzt mache:

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

Funktioniert, aber nicht wie es funktionieren soll.


stackoverflow.com/a/4198569/2382964 , Hallo Jasper, bitte verweisen Sie auf diesen Link ... dies wird Ihnen helfen.
Tushar Pandey

Versuchen Sie andere Methoden wie notifyItemInserted, notifyItemRemoved usw.
Reejesh PK

Antworten:


334

Ändern Sie Ihre Methode von

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

Zu

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

Sie behalten also dasselbe Objekt wie Ihr DataSet in Ihrem Adapter.


Überlegen Sie, ob Sie ein übergeordnetes Objekt ReceiptListObjectanstelle eines Listvon Objekten haben möchten. Was können Sie dann tun, um dieses Problem zu lösen?
Prom85

@ prom85 können ArrayAdapters sogar an andere Objekte als Listen oder Arrays binden? Ich wusste es nicht.
Tolgap

Es geht um a BaseAdapterund dieser Adapter weiß nicht, an welche Daten er gebunden ist. Wenn ich also ein benutzerdefiniertes Objekt habe und benutzerdefinierte Funktionen dieses Objekts (wie custObject.getCount()und custObject.getChildAt(int i)zum Beispiel) verwende und dieses Objekt austauschen möchte, funktioniert dies notifyDataSetChangednicht ... sowieso denke ich, dass dieses Problem nie mit einemArrayAdapter
prom85

3
Können Sie vielleicht erklären, warum die erste Methode nicht funktioniert und die zweite mit einem BaseAdapter?
Tooroop

1
Kommentare wie Danke oder +1 sind nicht erlaubt, aber bei einigen Antworten möchte ich mich wirklich bedanken :)
Muhammad Saqib

24

Ich habe das gleiche Problem und das merke ich. Wenn wir einen Adapter erstellen und auf Listenansicht setzen, zeigt die Listenansicht auf ein Objekt im Speicher, das der Adapter enthält. Die Daten in diesem Objekt werden in der Listenansicht angezeigt.

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

Wenn wir erneut ein Objekt für den Adapter mit anderen Daten erstellen und notifydatasetchanged ():

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

Dies wirkt sich nicht auf Daten in der Listenansicht aus, da die Liste auf ein anderes Objekt verweist, dieses Objekt nichts über neue Objekte im Adapter weiß und notifyDataSetChanged () keine Auswirkungen hat. Wir sollten also die Daten im Objekt ändern und vermeiden, erneut ein neues Objekt für den Adapter zu erstellen


10
Wenn

2
@Alezis Ich denke, genau das hat Nhan gemeint.
Neeraj Sewani

17

Wie ich bereits erklärt habe, die Gründe für dieses Problem und auch, wie man damit umgeht, in einem anderen Antwort-Thread hier . Trotzdem teile ich hier die Lösungszusammenfassung.

Einer der Hauptgründe, warum Sie notifyDataSetChanged()nicht arbeiten, ist:

Ihr Adapter verliert den Verweis auf Ihre Liste .

Beim Erstellen und Hinzufügen einer neuen Liste zur Adapter. Befolgen Sie immer diese Richtlinien:

  1. Initialisieren Sie das, arrayListwährend Sie es global deklarieren.
  2. Fügen Sie die Liste direkt zum Adapter hinzu, ohne nach Null- und Leerwerten zu suchen. Stellen Sie den Adapter direkt auf die Liste ein (prüfen Sie nicht, ob eine Bedingung vorliegt). Der Adapter garantiert Ihnen, dass er sich überall dort, wo Sie Änderungen an den Daten des Adapters vornehmen arrayList, darum kümmert, aber niemals die Referenz verliert.
  3. Ändern Sie immer die Daten in der ArrayList selbst (wenn Ihre Daten vollständig neu sind, als Sie aufrufen können, adapter.clear()und arrayList.clear()bevor Sie tatsächlich Daten zur Liste hinzufügen), aber stellen Sie den Adapter nicht ein, dh wenn die neuen Daten in der arrayListals nur gefüllt sindadapter.notifyDataSetChanged()

Hoffe das hilft.


1
Vielen Dank! Der Tipp auf # 2 hat geholfen.
Cheruby

4

Versuchen Sie möglicherweise, Ihre ListView zu aktualisieren:

receiptsListView.invalidate().

EDIT: Ein anderer Gedanke kam mir in den Sinn. Versuchen Sie nur für den Datensatz, den Listenansichts-Cache zu deaktivieren:

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

Seltsam, meine letzte Idee ist es, den Adapter der Listenansicht zuzuweisen, nachdem die Daten geändert wurden. Aber ich nehme an, Sie haben das auch schon versucht.
Rafal Gałka

3

Ich hatte das gleiche Problem mit ListAdapter

Ich habe Android Studio Methoden für mich implementieren lassen und Folgendes habe ich:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Das Problem ist, dass diese Methoden keine superImplementierungen aufrufen und daher notifyDataSetChangeniemals aufgerufen werden.

Entfernen Sie diese Überschreibungen entweder manuell oder fügen Sie Superaufrufe hinzu, und es sollte wieder funktionieren.

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

Du kannst es versuchen. Es funktioniert für mich



1

Wenn Sie zufällig auf diesem Thread gelandet sind und sich gefragt haben, warum adapter.invaidate()oder adapter.clear()Methoden in Ihrem Fall nicht vorhanden sind, dann möglicherweise, weil Sie möglicherweise RecyclerView.Adapterstattdessen BaseAdapterverwenden, welche vom Fragesteller dieser Frage verwendet wird. Wenn Sie das Problem beheben listoder arraylistnicht lösen, kann es vorkommen, dass Sie zwei oder mehr Instanzen des Beispielsadapter erstellen:

Hauptaktivität

...

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

...

und
SomeFragment

...

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

...

Wenn Sie im zweiten Fall eine Änderung in der Liste der aufgeblasenen Ansichten in der Recycler-Ansicht erwarten, geschieht dies nicht, da beim zweiten Mal eine neue Instanz von adaptererstellt wird, die nicht an die Recycler-Ansicht angehängt ist. Durch die Einstellung notifyDataSetChangedim zweiten Adapter wird der Inhalt der Recycling-Ansicht nicht geändert. Erstellen Sie dazu eine neue Instanz der Recycler-Ansicht in SomeFragment und hängen Sie sie an die neue Instanz des Adapters an.

SomeFragment

...

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

...

Ich empfehle jedoch nicht, mehrere Instanzen desselben Adapters und derselben Recycler-Ansicht zu erstellen.


0

Fügen Sie diesen Code hinzu

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

0

Ich habe das gleiche Problem, aber ich habe es gerade beendet !!

du solltest zu wechseln

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

Mein Fall war anders, aber für andere könnte es der gleiche sein

Für diejenigen, die immer noch keine Lösung finden und alles oben Genannte ausprobieren konnten: Wenn Sie den Adapter im Fragment verwenden, ist dies der Grund, warum er nicht funktioniert fragment could be recreating so the adapter is recreating everytime the fragment recreate

Sie sollten vor der Initialisierung überprüfen, ob der Adapter und die Objektliste null sind

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.