To refresh the ListView in Android, call notifyDataSetChanged() method on the Adapter that has been set with the ListView. Also note that the method notifyDataSetChanged() has to be called on UI thread.
Just clone and open the project in Android Studio (gradle). This project has a MainAcitivity building a ListView with all random data. This list can be refreshed using the action menu.
It tells the ListView that the data has been modified; and to show the new data, the ListView must be redrawn.
notifyDataSetChanged. Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
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<Item> items) {
this.items = items;
notifyDataSetChanged();
}
4) change your onResume to:
@Override
public void onResume() {
super.onResume();
adapter.swapItems(dbHelper.getItems());
}
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);**
}
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.
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()
}
});
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 : ).
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!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With