notifyDataSetChange not working from custom adapter
Asked Answered
D

14

152

When I repopulate my ListView, I call a specific method from my Adapter.

Problem:

When I call updateReceiptsList from my Adapter, the data is refreshed, but my ListView doesn't reflect the change.

Question:

Why doesn't my ListView show the new data when I call 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;
    }

}

--EDIT:

found Workaround

Just to have some functional code i do now:

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

Works, but not how it is supposed to work.

Darn answered 14/3, 2013 at 23:29 Comment(2)
https://mcmap.net/q/160031/-updating-the-list-view-when-the-adapter-data-changes ,Hi Jasper please refer this link ... this will help you .Marking
try other methods like notifyItemInserted, notifyItemRemoved,etc..Heideheidegger
B
374

Change your method from

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

To

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

So you keep the same object as your DataSet in your Adapter.

Balinese answered 15/3, 2013 at 8:28 Comment(12)
consider having a parent object like ReceiptListObject instead of a List of objects, what can you do then to solve this problem?Kiln
@Kiln can ArrayAdapters even bind to Objects other than Lists or Arrays? I did not know.Balinese
it's about a BaseAdapter and this adapter does not know to which data it is binded... so if I have an custom object and use custom functions of this object (like custObject.getCount() and custObject.getChildAt(int i) for example), and I want to exchange this object, notifyDataSetChanged is not working... anyway, I think this problem does never occur with an ArrayAdapterKiln
btw, are you talking about the ArrayAdapter only? There your answer makes sense... I would have no idea why exchanging the list object should make a problem in a BaseAdapter... But I can't get a BaseAdapter to work properly, without resetting the adapter instead of calling notifyDataSetChanged...×Comments may only be edited for 5 minutes×Comments may only be edited for 5 minutes×Comments may only be edited for 5 minutesKiln
Can you maybe explain why the first method doesn't work and the second works with a BaseAdapter?Wyattwyche
@Balinese this question was directed to you :) The first solution worked for me for some time, but then it stopped working, and I had to change the code like in the second method. Now it's working fine.Wyattwyche
@Wyattwyche I can't imagine the first solution working, because all you do is change the reference of the receiptlist variable. The BaseAdapter has no knowledge of this, so notifyDataSetChanged() would not change anything. You should work on your initial reference List and remove/add items to that List.Balinese
Somehow It is not working with a static arraylist of mine. I have 2 adapters In first This works like a charm. But in 2nd Same Adapter it gives a force close says ArrayOutOfIndex and notifydatasetchanged not workingRenascence
@Wyattwyche I had used the second way by clearing and add new items. But changing it to first way worked for me. Very strange.Busse
comments like Thanks or +1 are not allowed but on some answers I really like to give a thank :)Unshapen
Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? #66827914Cara
I think in Kotlin at least you will need to use a MutableList to get clear() and addAll().Inmate
P
27

I have the same problem, and i realize that. When we create adapter and set it to listview, listview will point to object somewhere in memory which adapter hold, data in this object will show in listview.

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

if we create an object for adapter with another data again and notifydatasetchanged():

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

this will do not affect to data in listview because the list is pointing to different object, this object does not know anything about new object in adapter, and notifyDataSetChanged() affect nothing. So we should change data in object and avoid to create a new object again for adapter

Porism answered 21/4, 2014 at 10:31 Comment(3)
you are recreating the adapter object, it is not efficientMarymarya
@Marymarya I think that's exactly what Nhan meant.Lakin
Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? #66827914Cara
B
22

As I have already explained the reasons behind this issue and also how to handle it in a different answer thread Here. Still i am sharing the solution summary here.

One of the main reasons notifyDataSetChanged() won't work for you - is,

Your adapter loses reference to your list.

When creating and adding a new list to the Adapter. Always follow these guidelines:

  1. Initialise the arrayList while declaring it globally.
  2. Add the List to the adapter directly with out checking for null and empty values . Set the adapter to the list directly (don't check for any condition). Adapter guarantees you that wherever you make changes to the data of the arrayList it will take care of it, but never loose the reference.
  3. Always modify the data in the arrayList itself (if your data is completely new than you can call adapter.clear() and arrayList.clear() before actually adding data to the list) but don't set the adapter i.e If the new data is populated in the arrayList than just adapter.notifyDataSetChanged()

Hope this helps.

Boutique answered 1/8, 2016 at 6:37 Comment(2)
Thanks! The tip at #2 helped.Guncotton
Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? #66827914Cara
R
4

Maybe try to refresh your ListView:

receiptsListView.invalidate().

EDIT: Another thought came into my mind. Just for the record, try to disable list view cache:

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />
Rivulet answered 15/3, 2013 at 0:18 Comment(1)
Weird, my last idea is to reassign adapter to list view after data changed. But I suppose you already tried this too.Tranquillity
R
4

I had the same problem using ListAdapter

I let Android Studio implement methods for me and this is what I got:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

The problem is that these methods do not call super implementations so notifyDataSetChange is never called.

Either remove these overrides manually or add super calls and it should work again.

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

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    super.unregisterDataSetObserver(observer);
}
Rhineland answered 22/12, 2015 at 10:10 Comment(1)
Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? #66827914Cara
C
2

If adapter is set to AutoCompleteTextView then notifyDataSetChanged() doesn't work.

Need this to update adapter:

myAutoCompleteAdapter = new ArrayAdapter<String>(MainActivity.this, 
        android.R.layout.simple_dropdown_item_1line, myList);

myAutoComplete.setAdapter(myAutoCompleteAdapter);

Refer: http://android-er.blogspot.in/2012/10/autocompletetextview-with-dynamic.html

Crotch answered 13/12, 2017 at 9:26 Comment(1)
Thanks. This worked. But I didn't get any answer why notifyDataSetChanged() does not work with AutoCompleteTextView.Duero
L
2

If by any chance you landed on this thread and wondering why adapter.invaidate() or adapter.clear() methods are not present in your case then maybe because you might be using RecyclerView.Adapter instead of BaseAdapter which is used by the asker of this question. If clearing the list or arraylist not resolving your problem then it may happen that you are making two or more instances of the adapter for ex.:

MainActivity

...

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

...

and
SomeFragment

...

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

...

If in the second case you are expecting a change in the list of inflated views in recycler view then it is not gonna happen as in the second time a new instance of the adapter is created which is not attached to the recycler view. Setting notifyDataSetChanged in the second adapter is not gonna change the content of recycer view. For that make a new instance of the recycler view in SomeFragment and attach it to the new instance of the adapter.

SomeFragment

...

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

...

Although, I don't recommend making multiple instances of the same adapter and recycler view.

Lakin answered 30/5, 2019 at 11:37 Comment(0)
A
1
class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

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

You can try. It work for me

Achene answered 27/1, 2019 at 17:32 Comment(0)
J
1

In my case I simply forget to add in my fragment mRecyclerView.setAdapter(adapter)

Justitia answered 22/11, 2020 at 21:0 Comment(0)
M
0

Add this code

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });
Mosasaur answered 22/3, 2017 at 10:32 Comment(0)
E
0

I made a very noob mistake that I was setting the adapter of RecyclerView before initialzing the adapter itself like this.

    // Assuume oneOffJobTasksListRVAdapter is declared already 
    recyclerView.setAdapter(oneOffJobTasksListRVAdapter);        
    oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();

Switching the lines fixed my issue.

    oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
    recyclerView.setAdapter(oneOffJobTasksListRVAdapter);        
Exeter answered 1/4, 2021 at 22:1 Comment(0)
F
-1

I have the same problem but I just finished it!!

you should change to

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

}
Francium answered 16/5, 2019 at 8:24 Comment(0)
M
-1

My case was different but it might be the same case for others

for those who still couldn't find a solution and tried everything above, if you're using the adapter inside fragment then the reason it's not working fragment could be recreating so the adapter is recreating everytime the fragment recreate

you should verify if the adapter and objects list are null before initializing

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

if(objects == null){
  objects = new ArrayList<>();
}
Mucoviscidosis answered 24/4, 2020 at 13:56 Comment(0)
W
-1

If you're using a custom adapter you have to add

@Override
public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
}

to your custom adapter methods, then you only need to call notifyDataSetChanged() after you change your data, like replace, remove or add a new item

ArrayList <String> items;
int position=1;
items.set(position,"Changed Item");
items.remove(position);
items.add("New item");
notifyDataSetChanged();
Woodshed answered 25/5, 2022 at 6:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.