Android ListView not refreshing after notifyDataSetChanged

AndroidAndroid ListviewNotifydatasetchanged

Android Problem Overview


My ListFragment code

public class ItemFragment extends ListFragment {

	private DatabaseHandler dbHelper;
	private static final String TITLE = "Items";
	private static final String LOG_TAG = "debugger";
	private ItemAdapter adapter;
	private List<Item> items;


	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.item_fragment_list, container, false);		
		return view;
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.setHasOptionsMenu(true);
		super.onCreate(savedInstanceState);
        getActivity().setTitle(TITLE);
        dbHelper = new DatabaseHandler(getActivity());
        items = dbHelper.getItems(); 
        adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
        this.setListAdapter(adapter);

	}

	

    @Override
    public void onResume() {
        super.onResume();
        items.clear();
        items = dbHelper.getItems(); //reload the items from database
        adapter.notifyDataSetChanged();
    }

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		super.onListItemClick(l, v, position, id);
		if(dbHelper != null) { //item is edited
			Item item = (Item) this.getListAdapter().getItem(position);
            Intent intent = new Intent(getActivity(), AddItemActivity.class);
            intent.putExtra(IntentConstants.ITEM, item);
            startActivity(intent);
		}
	}
}

My ListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
</LinearLayout>

But this does not refresh the ListView. Even after restarting app the updated items are not shown. My ItemAdapter extends BaseAdapter

public class ItemAdapter extends BaseAdapter{

    private LayoutInflater inflater;
	private List<Item> items;
	private Context context;

	public ProjectListItemAdapter(Context context, List<Item> items) {
	    super();
		inflater = LayoutInflater.from(context);
		this.context = context;
	    this.items = items;

	}

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

	@Override
	public Object getItem(int position) {
		return items.get(position);
	}

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

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
	    ItemViewHolder holder = null;
	    if(convertView == null) {
	        holder = new ItemViewHolder();
	        convertView = inflater.inflate(R.layout.list_item, parent,false);
	        holder.itemName = (TextView) convertView.findViewById(R.id.topText);
	        holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
	        convertView.setTag(holder);
	    } else {
	        holder = (ItemViewHolder) convertView.getTag();
	    }
	    holder.itemName.setText("Name: " + items.get(position).getName());
	    holder.itemLocation.setText("Location: " + items.get(position).getLocation());
	    if(position % 2 == 0) {                                                                                 
            convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
		} else {	
	        convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
		}
	    return convertView;
	}

	private static class ItemViewHolder {
	    TextView itemName;
		TextView itemLocation;
	}
}

Can someone help please?

Android Solutions


Solution 1 - Android

Look at your onResume method in ItemFragment:

@Override
public void onResume() {
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); // reload the items from database
    adapter.notifyDataSetChanged();
}

what you just have updated before calling notifyDataSetChanged() is not the adapter's field private List<Item> items; but the identically declared field of the fragment. The adapter still stores a reference to list of items you passed when you created the adapter (e.g. in fragment's onCreate). The shortest (in sense of number of changes) but not elegant way to make your code behave as you expect is simply to replace the line:

    items = dbHelper.getItems(); // reload the items from database

with

    items.addAll(dbHelper.getItems()); // reload the items from database

A more elegant solution:

  1. remove items private List<Item> items; from ItemFragment - we need to keep reference to them only in adapter

  2. change onCreate to :

    @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.setHasOptionsMenu(true); getActivity().setTitle(TITLE); dbHelper = new DatabaseHandler(getActivity()); adapter = new ItemAdapter(getActivity(), dbHelper.getItems()); setListAdapter(adapter); }

  3. add method in ItemAdapter:

    public void swapItems(List items) { this.items = items; notifyDataSetChanged(); }

  4. change your onResume to:

    @Override public void onResume() { super.onResume(); adapter.swapItems(dbHelper.getItems()); }

Solution 2 - Android

You are assigning reloaded items to global variable items in onResume(), but this will not reflect in ItemAdapter class, because it has its own instance variable called 'items'.

For refreshing ListView, add a refresh() in ItemAdapter class which accepts list data i.e items

class ItemAdapter
{
    .....

    public void refresh(List<Item> items)
    {
        this.items = items;
        notifyDataSetChanged();
    } 
}

update onResume() with following code

@Override
public void onResume()
{
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); //reload the items from database
    **adapter.refresh(items);**
}

Solution 3 - Android

In onResume() change this line

items = dbHelper.getItems(); //reload the items from database

to

items.addAll(dbHelper.getItems()); //reload the items from database

The problem is that you're never telling your adapter about the new items list. If you don't want to pass a new list to your adapter (as it seems you don't), then just use items.addAll after your clear(). This will ensure you are modifying the same list that the adapter has a reference to.

Solution 4 - Android

If the adapter is already set, setting it again will not refresh the listview. Instead first check if the listview has a adapter and then call the appropriate method.

I think its not a very good idea to create a new instance of the adapter while setting the list view. Instead, create an object.

BuildingAdapter adapter = new BuildingAdapter(context);

    if(getListView().getAdapter() == null){ //Adapter not set yet.
     setListAdapter(adapter);
    }
    else{ //Already has an adapter
    adapter.notifyDataSetChanged();
    }

Also you might try to run the refresh list on UI Thread:

activity.runOnUiThread(new Runnable() {         
        public void run() {
              //do your modifications here

              // for example    
              adapter.add(new Object());
              adapter.notifyDataSetChanged()  
        }
});

Solution 5 - Android

If you want to update your listview doesn't matter if you want to do that on onResume(), onCreate() or in some other function, first thing that you have to realize is that you won't need to create a new instance of the adapter, just populate the arrays with your data again. The idea is something similar to this :

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;

@Override
public void onCreate(Bundle savedInstanceState){
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main_activity);
	
	myListView = (ListView) findViewById(R.id.my_list);
	
	titles = new ArrayList<String>()

    for(int i =0; i<20;i++){
	    titles.add("Title "+i);
    }

    adapter = new MyListAdapter(this, titles);
    myListView.setAdapter(adapter);
}


@Override
public void onResume(){
	super.onResume();
	// first clear the items and populate the new items
	titles.clear();
	for(int i =0; i<20;i++){
		titles.add("New Title "+i);
	}
	adapter.notifySetDataChanged();
}

So depending on that answer you should use the same List<Item> in your Fragment. In your first adapter initialization you fill your list with the items and set adapter to your listview. After that in every change in your items you have to clear the values from the main List<Item> items and than populate it again with your new items and call notifySetDataChanged();.

That's how it works : ).

Solution 6 - Android

adpter.notifyDataSetInvalidated();

Try this in onPause() method of Activity class.

Solution 7 - Android

An answer from AlexGo did the trick for me:

getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
       	 messages.add(m);
       	 adapter.notifyDataSetChanged();
       	 getListView().setSelection(messages.size()-1);
        }
});

List Update worked for me before when the update was triggered from a GUI event, thus being in the UI thread.

However, when I update the list from another event/thread - i.e. a call from outside the app, the update would not be in the UI thread and it ignored the call to getListView. Calling the update with runOnUiThread as above did the trick for me. Thanks!!

Solution 8 - Android

Try this

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}

Solution 9 - Android

adapter.setNotifyDataChanged()

should do the trick.

Solution 10 - Android

If your list is contained in the Adapter itself, calling the function that updates the list should also call notifyDataSetChanged().

Running this function from the UI Thread did the trick for me:

The refresh() function inside the Adapter

public void refresh(){
    //manipulate list
    notifyDataSetChanged();
}

Then in turn run this function from the UI Thread

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});

Solution 11 - Android

Try like this:

this.notifyDataSetChanged();

instead of:

adapter.notifyDataSetChanged();

You have to notifyDataSetChanged() to the ListView not to the adapter class.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCoderView Question on Stackoverflow
Solution 1 - AndroidTomasz GawelView Answer on Stackoverflow
Solution 2 - AndroidSanthoshView Answer on Stackoverflow
Solution 3 - AndroidJustin BreitfellerView Answer on Stackoverflow
Solution 4 - AndroidAlexGoView Answer on Stackoverflow
Solution 5 - AndroidhardartcoreView Answer on Stackoverflow
Solution 6 - AndroidSomView Answer on Stackoverflow
Solution 7 - Androiduser2996950View Answer on Stackoverflow
Solution 8 - AndroidGautamiView Answer on Stackoverflow
Solution 9 - AndroidHitmanView Answer on Stackoverflow
Solution 10 - AndroidDévan CoetzeeView Answer on Stackoverflow
Solution 11 - AndroidJachuView Answer on Stackoverflow