Android: how to refresh ListView contents?
Asked Answered
L

9

38

My ListView is using an extension of BaseAdapter, I can not get it to refresh properly. When I refresh, it appears that the old data draws on top of the new data, until a scroll event happens. The old rows draw on top of the new rows, but the old rows disappear when I start scrolling.

I have tried calling invalidateViews(), notifyDataSetChanged(), and notifyDataSetInvalidated(). My code looks something like:

private void updateData()
{
   List<DataItems> newList = getNewList();

   MyAdapter adapter = new MyAdapter(getContext());
   //my adapter holds an internal list of DataItems
   adapter.setList(newList);
   mList.setAdapter(adapter);
   adapter.notifyDataSetChanged();
   mList.invalidateViews();
}
Leathery answered 4/2, 2011 at 23:0 Comment(3)
Did you ever find the answer to this question? I'm having exactly the same problem.Pokelogan
I also have a same problem, anyone solved it?Spellbinder
any one plse help me how to update new listdata with existing data onscrolllistenerRiesman
P
60

To those still having problems, I solved it this way:

List<Item> newItems = databaseHandler.getItems();
ListArrayAdapter.clear();
ListArrayAdapter.addAll(newItems);
ListArrayAdapter.notifyDataSetChanged();
databaseHandler.close();

I first cleared the data from the adapter, then added the new collection of items, and only then set notifyDataSetChanged(); This was not clear for me at first, so I wanted to point this out. Take note that without calling notifyDataSetChanged() the view won't be updated.

Penetration answered 30/9, 2012 at 16:39 Comment(4)
this helped a lot! My problem was that instead of ListArrayAdapter.clear(); I was creating a new empty ListAdapter again, and thus none of the other solutions were working.Necaise
I guess dbh is an instance of a database handler @akhyarSin
test and perhaps get rid of the statics, subclass and implement recyclingCosimo
It even works if you are do notifyDataSetChanged() inside the clear() and addAll() methods.Ogle
M
13

In my understanding, if you want to refresh ListView immediately when data has changed, you should call notifyDataSetChanged() in RunOnUiThread().

private void updateData() {
    List<Data> newData = getYourNewData();
    mAdapter.setList(yourNewList);

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
                mAdapter.notifyDataSetChanged();
        }
    });
}
Marja answered 29/2, 2012 at 1:47 Comment(4)
I've been combing StackOverflow for two hours, and found only hundreds of unhelpful "call notifyDataSetChanged" advice and the like. Your answer, though, actually works. Thanks!Oxymoron
It is saying that mAdapter should be final?Castanets
@FarazAhmad If mAdapter is local variable it should be final, but if mAdapter is instance(of Activity) variable, it doesn't have to be :)Marja
Tried a bunch of other solution on SO! This one worked perfectly! Thanks a ton, @Aigori!Abramabramo
P
12

You don't have to create a new adapter to update your ListView's contents. Simply store your Adapter in a field and update your list with the following code:

mAdapter.setList(yourNewList);
mAdapter.notifyDataSetChanged();

To clarify that, your Activity should look like that:

private YourAdapter mAdapter;

protected void onCreate(...) {

    ...

    mAdapter = new YourAdapter(this);
    setListAdapter(mAdapter);

    updateData();
}

private void updateData() {
    List<Data> newData = getYourNewData();
    mAdapter.setList(yourNewList);
    mAdapter.notifyDataSetChanged();
}
Parlance answered 4/2, 2011 at 23:40 Comment(6)
Hmm this code should work, could you post the part where you set your Adapter and the setList() method?Parlance
So, if my ListActivity is set to full screen (style/Theme.NoBackground) it has the issues that I described. But if I set it to Dialog theme, it refreshes properly.Leathery
well, the adapter is set as shown in my original post, with the setAdapter(...) method. and my setList() method doesn't do anything interesting, it just sets a member variable of the adapter, which is used to determine the view for each row.Leathery
@Leathery As I already said, you don't set the Adapter in your updateData() method. Instead set it in onCreate() and and call those methods described in my post on that Adapter.Parlance
thanks. sorry, I did try this as well. (which is what meant by "... i tried this without success.."), but I realize that comment was pretty vague.Leathery
Out of curiosity, what's the major downfall of setting the setAdapter() method every onResume(). Memory, performance, etc.?Sigler
L
4

I think refilling the same adapter with different data would be more or most better technique. Put this method in your Adapter class with right argument (the data list you want to display as names in my case) Call this where u update the data of list with updated list (names in my case)

public void refill(ArrayList<BeanDatabase> names) {
    list.clear();
    list.addAll(names);
    list.notifyDataSetChanged();
}

If you change the adapter or set the adapter again and again on when the list updates, then force close error would surely cause problems at some point. (Error:List data been updated but adapter doesn't notify the List View)

Luminous answered 16/2, 2012 at 5:37 Comment(0)
T
3

I too have tried invalidate(), invalidateViews(), notifyDataSetChanged(). They all might work in some particular contexts but it did not do the job in my case.

In my case, I had to add some new rows to the list and it just does not work. Creating a new adapter solved the issue.

While debugging, I realized that the data was there but just not rendered. If invalidate() or invalidateViews() does not render (which it is supposed to), I don't know what would.

Creating a new Adapter object to refresh modified data does not seem to be a bad idea. It definitely works. The only downside could be the time consumed in allocating new memory for your adapter but that should be OK assuming the Android system is smart enough and takes care of that to keep it efficient.

If you take this out of the equation, the flow is almost same as to calling notifyDataSetChanged. In the sense, the same set of adapter functions are called in either case.

So we are not losing much but gaining a lot.

Telltale answered 8/9, 2011 at 8:4 Comment(1)
I found setting a background color for the listview eliminated a lot of my refresh issues.Leathery
C
2

I'm doing the same thing using invalidateViews() and that works for me. If you want it to invalidate immediately you could try calling postInvalidate after calling invalidateViews.

Claudclauddetta answered 4/2, 2011 at 23:36 Comment(0)
P
2

Update ListView's contents by below code:

private ListView listViewBuddy;
private BuddyAdapter mBuddyAdapter;
private ArrayList<BuddyModel> buddyList = new ArrayList<BuddyModel>();

onCreate():

listViewBuddy = (ListView)findViewById(R.id.listViewBuddy);
mBuddyAdapter = new BuddyAdapter();
listViewBuddy.setAdapter(mBuddyAdapter);

onDataGet (After webservice call or from local database or otherelse):

mBuddyAdapter.setData(buddyList);
mBuddyAdapter.notifyDataSetChanged();

BaseAdapter:

private class BuddyAdapter extends BaseAdapter { 

    private ArrayList<BuddyModel> mArrayList = new ArrayList<BuddyModel>();
    private LayoutInflater mLayoutInflater= (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    private ViewHolder holder;

    public void setData(ArrayList<BuddyModel> list){
        mArrayList = list;
    }

    @Override
    public int getCount() {
        return mArrayList.size();
    }

    @Override
    public BuddyModel getItem(int position) {
        return mArrayList.get(position);
    }

    @Override
    public long getItemId(int pos) {
        return pos;
    }

    private class ViewHolder {
        private TextView txtBuddyName, txtBuddyBadge;

    }
    @SuppressLint("InflateParams")
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.row_buddy, null);
            // bind views
            holder.txtBuddyName = (TextView) convertView.findViewById(R.id.txtBuddyName);
            holder.txtBuddyBadge = (TextView) convertView.findViewById(R.id.txtBuddyBadge);

            // set tag
            convertView.setTag(holder);
        } else {
            // get tag
            holder = (ViewHolder) convertView.getTag();
        }

        holder.txtBuddyName.setText(mArrayList.get(position).getFriendId());

        int badge = mArrayList.get(position).getCount();
        if(badge!=0){
            holder.txtBuddyBadge.setVisibility(View.VISIBLE);
            holder.txtBuddyBadge.setText(""+badge);
        }else{
            holder.txtBuddyBadge.setVisibility(View.GONE);
        }   

        return convertView;
    }
}

Whenever you want to Update Listview just call below two lines code:

 mBuddyAdapter.setData(Your_Updated_ArrayList);
 mBuddyAdapter.notifyDataSetChanged();

Done

Pandybat answered 8/7, 2015 at 9:31 Comment(0)
L
1

Only this works for me everytime, note that I don't know if it causes any other complications or performance issues:

private void updateListView(){
        listview.setAdapter(adapter);
    }
Leverrier answered 25/8, 2015 at 14:30 Comment(0)
A
1

Another easy way:

//In your ListViewActivity:
public void refreshListView() {
    listAdapter = new ListAdapter(this);
    setListAdapter(listAdapter);
}
Analects answered 8/9, 2016 at 16:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.