Reputation: 70426
11-06 19:52:25.958: E/AndroidRuntime(29609): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(-1, class android.widget.ListPopupWindow$DropDownListView) with Adapter(class com.example.parkfoxxlight_android.PlacesAutoCompleteAdapter)]
Full log: http://pastebin.com/Hx7k28Rm
Full code of adapter: http://pastebin.com/TfH1bXE3 I am using the example from https://developers.google.com/places/training/autocomplete-android and it has quite the default code so it seems there is a bug in the google code?
The app crashes only sometimes with the above error message.
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
Activity http://pastebin.com/FYzYtvXY:
public class CityActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.city);
AutoCompleteTextView autoCompView = (AutoCompleteTextView) findViewById(R.id.autocomplete_city);
PlacesAutoCompleteAdapter ad = new PlacesAutoCompleteAdapter(this);
ProgressBar b = (ProgressBar)findViewById(R.id.progressBar1);
ad.setLoadingIndicator(b);
autoCompView.setAdapter(ad);
}
}
Any ideas how to fix this? I am on android 4.3.
Upvotes: 18
Views: 4701
Reputation: 87064
The Filter
's performFiltering()
method runs on a background thread and from that method you're changing the resultList
on which your adapter is based. If you change that list of data and in that time the ListView
access the adapter it will see that something has changed without its knowledge(and it will not be happy). You should avoid using the resultList
in the performFiltering
method and simply create a new temporary list:
// in the performFiltering method which runs on a background thread:
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<String> queryResults;
if (constraint != null && constraint.length() > 0) {
queryResults = autocomplete(constraint);
} else {
queryResults = new ArrayList<String>(); // empty list/no suggestions showing if there's no valid constraint
}
filterResults.values = queryResults;
filterResults.count = queryResults.size();
return filterResults; // ## Heading ##
}
private List<String> autocomplete(String input) {
// don't use the here the resultList List on which the adapter is based!
// some custom code to get items from http connection
ArrayList<String> queryResults = new ArrayList<String>(); // new list
queryResults.add("Some String");
return queryResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
// update the data with the new set of suggestions
resultList = (ArrayList<String>)results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
Upvotes: 37
Reputation: 93759
Try this (just a guess):
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
ArrayList list = autocomplete(constraint.toString());
if (list != null) {
filterResults.values = list;
filterResults.count = list.size();
}
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
//change the underlying data immediately before notifying UI
resultList = (ArrayList)results.values;
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}};
return filter;
}
Upvotes: 0