How to refresh Android ListView after adding / deleting dynamic data?
#1 building
Please ignore all invalid(), invalideviews(), requestLayout(),... Answers to this question.
The right approach (and fortunately marked as the right answer) is Call notifyDataSetChanged() on Adapter .
Troubleshooting
If the call to notifyDataSetChanged() doesn't work, all layout methods don't help either. I believe my ListView has been updated correctly. If no differences are found, you need to check the source of the data in the adapter.
If this is just a collection, you want to keep it in memory, and then check whether the items have been actually removed or added from the collection before calling notifyDataSetChanged().
If you are using a database or service backend, you must call this method before calling notifyDataSetChanged() to retrieve information again (or to manipulate data notifyDataSetChanged() in memory.
The thing is, notifyDataSetChanged is only valid if the dataset has changed. So if you can't find the changes, you can find them here. If debugging is required.
ArrayAdapter and BaseAdapter
I do find that using adapters to manage collections is as good as using BaseAdapter. Some adapters, such as ArrayAdapter, already manage their own collections, making it more difficult to get to the correct collection for updates. In most cases, this is actually just unnecessary extra difficulty.
UI thread
Indeed, this must be invoked from the UI thread. Other answers include examples of how to do this. However, this is only necessary if you are processing this information outside the UI thread. That's from a service or non UI thread. In a simple case, you will update the data by clicking a button or other activity / fragment. So it's still in the UI thread. It is not necessary to always pop up the runonuitread.
Quick sample project
Can be in https://github.com/hanscappelle/so-2250770.git Find it. Just clone and open the project (gradient) in Android Studio. The main activity of the project uses all the random data to build a ListView. You can refresh this list using the actions menu.
The adapter implementation I created for this sample ModelObject exposes data collection
public class MyListAdapter extends BaseAdapter { /** * this is our own collection of data, can be anything we * want it to be as long as we get the abstract methods * implemented using this data and work on this data * (see getter) you should be fine */ private List<ModelObject> mData; /** * our ctor for this adapter, we'll accept all the things * we need here * * @param mData */ public MyListAdapter(final Context context, final List<ModelObject> mData) { this.mData = mData; this.mContext = context; } public List<ModelObject> getData() { return mData; } // implement all abstract methods here }
Code from MainActivity
public class MainActivity extends Activity { private MyListAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView list = (ListView) findViewById(R.id.list); // create some dummy data here List<ModelObject> objects = getRandomData(); // and put it into an adapter for the list mAdapter = new MyListAdapter(this, objects); list.setAdapter(mAdapter); // mAdapter is available in the helper methods below and the // data will be updated based on action menu interactions // you could also keep the reference to the android ListView // object instead and use the {@link ListView#getAdapter()} // method instead. However you would have to cast that adapter // to your own instance every time } /** * helper to show what happens when all data is new */ private void reloadAllData(){ // get new modified random data List<ModelObject> objects = getRandomData(); // update data in our adapter mAdapter.getData().clear(); mAdapter.getData().addAll(objects); // fire the event mAdapter.notifyDataSetChanged(); } /** * helper to show how only changing properties of data * elements also works */ private void scrambleChecked(){ Random random = new Random(); // update data in our adapter, iterate all objects and // resetting the checked option for( ModelObject mo : mAdapter.getData()) { mo.setChecked(random.nextBoolean()); } // fire the event mAdapter.notifyDataSetChanged(); } }
More information
Another good article on listViews features is at: http ://www.vogella.com/articles/AndroidListView/article.html
#2 building
If you want to update the UI list view from a service, set the adapter to static in the Main activity, and then do the following:
@Override public void onDestroy() { if (MainActivity.isInFront == true) { if (MainActivity.adapter != null) { MainActivity.adapter.notifyDataSetChanged(); } MainActivity.listView.setAdapter(MainActivity.adapter); } }
#3 building
For me, after changing the information in the sql database, nothing can refresh the list view (a specific extensible list view). Therefore, if notifyDataSetChanged() cannot solve the problem, you can try to clear the list first, and then add the list again after calling notifyDataSetChanged(). for example
private List<List<SomeNewArray>> arrayList; List<SomeNewArray> array1= getArrayList(...); List<SomeNewArray> array2= getArrayList(...); arrayList.clear(); arrayList.add(array1); arrayList.add(array2); notifyDataSetChanged();
I hope it makes sense to you.
#4 building
I couldn't get notifyDataSetChanged() to update my SimpleAdapter, so I tried to first remove all views attached to the parent layout using removeAllViews(), and then add a ListView, which works properly, allowing me to update the user interface:
LinearLayout results = (LinearLayout)findViewById(R.id.results); ListView lv = new ListView(this); ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>(); SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } ); for (...) { HashMap<String, String> map = new HashMap<String, String>(); map.put("name", name); map.put("dept", dept); list.add(map); } lv.setAdapter(adapter); results.removeAllViews(); results.addView(lv);
#5 building
When using SimpleCursorAdapter, you can call changeCursor (newCursor) on the adapter.