150

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.

Jasper
  • 2,389
  • 4
  • 25
  • 40

14 Answers14

371

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.

tolgap
  • 9,629
  • 10
  • 51
  • 65
  • consider having a parent object like `ReceiptListObject` instead of a `List` of objects, what can you do then to solve this problem? – prom85 Feb 10 '14 at 17:38
  • @prom85 can ArrayAdapters even bind to Objects other than Lists or Arrays? I did not know. – tolgap Feb 10 '14 at 17:44
  • 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 `ArrayAdapter` – prom85 Feb 10 '14 at 17:49
  • 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 minutes – prom85 Feb 10 '14 at 18:02
  • 3
    Can you maybe explain why the first method doesn't work and the second works with a BaseAdapter? – Tooroop Mar 29 '14 at 13:05
  • @tolgap 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. – Tooroop Mar 29 '14 at 18:03
  • @Tooroop 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`. – tolgap Mar 29 '14 at 20:19
  • 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 working – Cyph3rCod3r Nov 21 '14 at 06:31
  • @Tooroop I had used the second way by clearing and add new items. But changing it to first way worked for me. Very strange. – Sarthak Majithia Jun 19 '15 at 09:19
  • 2
    comments like Thanks or +1 are not allowed but on some answers I really like to give a thank :) – Muhammad Saqib Mar 24 '19 at 12:15
  • Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? https://stackoverflow.com/questions/66827414/android-update-listview-through-baseadapter-in-postexecute-not-working – stefanosn Mar 27 '21 at 11:13
  • I think in Kotlin at least you will need to use a MutableList to get clear() and addAll(). – Chucky Jan 06 '22 at 16:08
26

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

Nhan Tran
  • 531
  • 6
  • 9
  • 16
    you are recreating the adapter object, it is not efficient – Alezis Feb 06 '18 at 12:07
  • 3
    @Alezis I think that's exactly what Nhan meant. – Neeraj Sewani May 30 '19 at 11:16
  • Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? https://stackoverflow.com/questions/66827414/android-update-listview-through-baseadapter-in-postexecute-not-working – stefanosn Mar 27 '21 at 11:27
21

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.

Community
  • 1
  • 1
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
  • 1
    Thanks! The tip at #2 helped. – Tiffany Jul 13 '17 at 13:36
  • Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? https://stackoverflow.com/questions/66827414/android-update-listview-through-baseadapter-in-postexecute-not-working – stefanosn Mar 27 '21 at 11:27
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"
    ... />
Rafal Gałka
  • 1,022
  • 1
  • 10
  • 16
  • Weird, my last idea is to reassign adapter to list view after data changed. But I suppose you already tried this too. – Rafal Gałka Mar 15 '13 at 07:41
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);
}
Davor Zlotrg
  • 6,020
  • 2
  • 33
  • 51
  • Hello everyone i use these methods you mention but does not work for me. Could you please anyone check my question? https://stackoverflow.com/questions/66827414/android-update-listview-through-baseadapter-in-postexecute-not-working – stefanosn Mar 27 '21 at 11:27
1

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

Prabs
  • 4,923
  • 6
  • 38
  • 59
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

M Reza
  • 18,350
  • 14
  • 66
  • 71
1

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.

Neeraj Sewani
  • 3,952
  • 6
  • 38
  • 55
1

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

Loic Jackotin
  • 245
  • 2
  • 10
0

Add this code

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });
Gowthaman M
  • 8,057
  • 8
  • 35
  • 54
ShriKant A
  • 105
  • 5
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);        
-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;
    }

}
Neeraj Sewani
  • 3,952
  • 6
  • 38
  • 55
林權章
  • 19
  • 5
-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<>();
}
Farido mastr
  • 464
  • 5
  • 12
-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();