I am new to Android programming, but not to programming in general or Java. My question here is mainly about best practices.
I created a class MenuListActivity that extends a ListActivity that shows menu items in a list and starts another Activity when clicked. Each menu is a child class of the MenuListActivity class and implements a getMenuItems() method to create the menu items.
In my onCreate() method, I call that getMenuItems() to create a MenuListAdapter class that is then set as the adapter of the ListActivity's ListView.
However, sometimes I get an illegalStateException telling me that "The content of the adapter has changed but ListView did not receive a notification.". Notice it happens sometimes, not all the times. I do not touch the adapter in any other thread, in fact the adapter is set like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<MenuAdapterItem> menuItems = getMenuItems();
this.setListAdapter(new MenuAdapter(this, menuItems));
}
Another possibility I can think of, is to create an Adapter member variable and call notifyDataSetChanged() on each update.
What would be the way to do this correctly? (And hopefully fix my crashes ;-)).
Regards
Bart
i ran into this problem before and i used "setNotifyOnChange(true);" hope that helps.
Related
I'm trying to use in my Android Application the notifyDataSetChanged() method for an ArrayAdapter but it doesn't work for me.
I found as answer here, that notifyDataSetChanged() should run in the main thread, but there was no example for that.
Could anybody send an example or at least a link?!
For an ArrayAdapter, notifyDataSetChanged only works if you use the add(), insert(), remove(), and clear() on the Adapter.
When an ArrayAdapter is constructed, it holds the reference for the List that was passed in. If you were to pass in a List that was a member of an Activity, and change that Activity member later, the ArrayAdapter is still holding a reference to the original List. The Adapter does not know you changed the List in the Activity.
Your choices are:
Use the functions of the ArrayAdapter to modify the underlying List (add(), insert(), remove(), clear(), etc.)
Re-create the ArrayAdapter with the new List data. (Uses a lot of resources and garbage collection.)
Create your own class derived from BaseAdapter and ListAdapter that allows changing of the underlying List data structure.
Use the notifyDataSetChanged() every time the list is updated. To call it on the UI-Thread, use the runOnUiThread() of Activity.
Then, notifyDataSetChanged() will work.
You can use the runOnUiThread() method as follows. If you're not using a ListActivity, just adapt the code to get a reference to your ArrayAdapter.
final ArrayAdapter adapter = ((ArrayAdapter)getListAdapter());
runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
});
I recently wrote on this topic, though this post it old, I thought it will be helpful to someone who wants to know how to implement BaseAdapter.notifyDataSetChanged() step by step and in a correct way.
Please follow How to correctly implement BaseAdapter.notifyDataSetChanged() in Android or the newer blog BaseAdapter.notifyDataSetChanged().
I had the same problem and I prefer not to replace the entire ArrayAdapter with a new instance continuously. Thus I have the AdapterHelper do the heavy lifting somewhere else.
Add this where you would normally (try to) call notify
new AdapterHelper().update((ArrayAdapter)adapter, new ArrayList<Object>(yourArrayList));
adapter.notifyDataSetChanged();
AdapterHelper class
public class AdapterHelper {
#SuppressWarnings({ "rawtypes", "unchecked" })
public void update(ArrayAdapter arrayAdapter, ArrayList<Object> listOfObject){
arrayAdapter.clear();
for (Object object : listOfObject){
arrayAdapter.add(object);
}
}
}
I know this is a late response but I was facing a similar issue and I managed to solve it by using notifyDataSetChanged() in the right place.
So my situation was as follows.
I had to update a listview in an action bar tab (fragment) with contents returned from a completely different activity. Initially however, the listview would not reflect any changes. However, when I clicked another tab and then returned to the desired tab,the listview would be updated with the correct content from the other activity. So to solve this I used notifyDataSetChanged() of the action bar adapter in the code of the activity which had to return the data.
This is the code snippet which I used in the activity.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.action_new_forward:
FragmentTab2.mListAdapter.notifyDataSetChanged();//this updates the adapter in my action bar tab
Intent ina = new Intent(getApplicationContext(), MainActivity.class);
ina.putExtra("stra", values1);
startActivity(ina);// This is the code to start the parent activity of my action bar tab(fragment).
}
}
This activity would return some data to FragmentTab2 and it would directly update my listview in FragmentTab2.
Hope someone finds this useful!
I am trying to create a class that uses both a ListView and MapView. I originally wrote the class to extend ListActivity. However, I later found out that I would need to extend MapView and then reference the ListView if I wanted to combine the two.
So, this is how is is now declared:
public class MapListActivity extends MapActivity {
Next I figured I needed a way to get at the ListView so I wrote this:
//instance vars
ListView listView;
//get ref to listview
listView = (ListView) findViewById(android.R.id.list);
In line with the old code I added:
listView.setListAdapter(adapter);
// selecting single ListView item
listView.getListView();
However, the error I get is that the method setListAdapter(ListAdapter) is undefined for the type MapListActivity or ListView. This makes sense for the MapView because the class extends MapActivity but I am not sure how to get it to work now that the class no longer extends ListActivity?
Any help appreciated.
Read the ListView documentation
listView.setListAdapter(adapter);
should be
listView.setAdapter(adapter);
Also,
listView.getListView();
doesn't exist for a ListView. It is unclear what you want to achieve. If you want to get the item at a position, you can use ListView#getItemAtPosition (int pos)
I have an Activity Class with an inner protected class that extends AsyncTask. I am trying to have the AsyncTask load data in background when I click a button and then display a list based on that data.
here is my code for the setListAdapter:
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_row,
R.id.rowtextview, testArr.toArray(test2)));
the problem is that I can't just stick setListAdapter into the AsyncTask class because it needs a context for the outer class, and the only place it can go is inside the onCreate() in the outer Activity class. If its inside the onCreate, it can only be used once and isnt dynamic. I am not sure how I would go about making the list re-load every time I click a button and search for something new.
Your custom inner AsyncTask is not marked as static right? That means that it actually holds reference to the parent class, which is you Activity. So you can use:
setListAdapter(new ArrayAdapter<String>(YourActivity.this, R.layout.list_row,
R.id.rowtextview, testArr.toArray(test2)));
Easy, make the adapter an instance variable and extend it (trivial) to add your custom updateData(newData) method. In this method you update your data AND call notifyDatasetChanged(). And you're done! That way you only need to set the adapter once and just call updateData() when the AsyncTask comes back.
I'm trying to use in my Android Application the notifyDataSetChanged() method for an ArrayAdapter but it doesn't work for me.
I found as answer here, that notifyDataSetChanged() should run in the main thread, but there was no example for that.
Could anybody send an example or at least a link?!
For an ArrayAdapter, notifyDataSetChanged only works if you use the add(), insert(), remove(), and clear() on the Adapter.
When an ArrayAdapter is constructed, it holds the reference for the List that was passed in. If you were to pass in a List that was a member of an Activity, and change that Activity member later, the ArrayAdapter is still holding a reference to the original List. The Adapter does not know you changed the List in the Activity.
Your choices are:
Use the functions of the ArrayAdapter to modify the underlying List (add(), insert(), remove(), clear(), etc.)
Re-create the ArrayAdapter with the new List data. (Uses a lot of resources and garbage collection.)
Create your own class derived from BaseAdapter and ListAdapter that allows changing of the underlying List data structure.
Use the notifyDataSetChanged() every time the list is updated. To call it on the UI-Thread, use the runOnUiThread() of Activity.
Then, notifyDataSetChanged() will work.
You can use the runOnUiThread() method as follows. If you're not using a ListActivity, just adapt the code to get a reference to your ArrayAdapter.
final ArrayAdapter adapter = ((ArrayAdapter)getListAdapter());
runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
});
I recently wrote on this topic, though this post it old, I thought it will be helpful to someone who wants to know how to implement BaseAdapter.notifyDataSetChanged() step by step and in a correct way.
Please follow How to correctly implement BaseAdapter.notifyDataSetChanged() in Android or the newer blog BaseAdapter.notifyDataSetChanged().
I had the same problem and I prefer not to replace the entire ArrayAdapter with a new instance continuously. Thus I have the AdapterHelper do the heavy lifting somewhere else.
Add this where you would normally (try to) call notify
new AdapterHelper().update((ArrayAdapter)adapter, new ArrayList<Object>(yourArrayList));
adapter.notifyDataSetChanged();
AdapterHelper class
public class AdapterHelper {
#SuppressWarnings({ "rawtypes", "unchecked" })
public void update(ArrayAdapter arrayAdapter, ArrayList<Object> listOfObject){
arrayAdapter.clear();
for (Object object : listOfObject){
arrayAdapter.add(object);
}
}
}
I know this is a late response but I was facing a similar issue and I managed to solve it by using notifyDataSetChanged() in the right place.
So my situation was as follows.
I had to update a listview in an action bar tab (fragment) with contents returned from a completely different activity. Initially however, the listview would not reflect any changes. However, when I clicked another tab and then returned to the desired tab,the listview would be updated with the correct content from the other activity. So to solve this I used notifyDataSetChanged() of the action bar adapter in the code of the activity which had to return the data.
This is the code snippet which I used in the activity.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.action_new_forward:
FragmentTab2.mListAdapter.notifyDataSetChanged();//this updates the adapter in my action bar tab
Intent ina = new Intent(getApplicationContext(), MainActivity.class);
ina.putExtra("stra", values1);
startActivity(ina);// This is the code to start the parent activity of my action bar tab(fragment).
}
}
This activity would return some data to FragmentTab2 and it would directly update my listview in FragmentTab2.
Hope someone finds this useful!
The problem I am facing is stated below in code. I am trying to call a 'method' from another class, which is going good until it reaches a activity (like getListView() or getAssets())
Calling class:
public class Main extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Utils ut = new Utils();
ut.initiateMainMenu();
}
}
Called class (method: initiateMainMenu):
public class Utils extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initiateMainMenu();
}
public void initiateMainMenu() {
listView = getListView();
assetManager = getAssets();
etc...
}
}
In the example above a nullpointer occurs when the first class (Main) is started with an intent.
The nullpointer is given on the following lines depending on which line comes first:
listView = getListView();
assetManager = getAssets();
The same nullpointer does not occur when class Utils is directly intended.
I hope the above description is suffient for solving my problem.
Kind regards,
Conrad
If you are holding a list view in your main activity, instead of doing this, which seems really weird to me, add the list view to R.layout.main and extend your class from ListActivity. You can get rid of Utils, and have all the listview related code in Main.
Or maybe some more information about what you pretend to implement might help...
Ger
Well the solution is simple, Your context for Utils class is never initialized, i.e. Utils class's onCreate method is never called or is out of focus or due to some other reason your ListView is never initialized.
You could do following :
Inverse calls and hold handle for list, may be using static keyword.
Or may be try changing code to getApplicationContext.getListView() / Utils.this.getListView of your initiateMainMenu();
I dont know to what extend this would work, but the basic cause behind this issue is uninitialized listView resulting in NULL.
The difficulty I was facing was not entirely described because my Java knowledge was not sufficient for identifying it as a problem.
What I was doing was using three Activities that inherit from each other. Namely Activity, MenuActivity (my own extended one) and ListActivity. The quick and dirty solution I am now using is a duplicate class. One class holds MenuActivity extending Activity and the other holds MenuActivity extending ListActivity.