Ошибка использования notifyDataSetChanged в аэродромном массиве
11-06 19: 52: 25.958: E/AndroidRuntime (29609): java.lang.IllegalStateException: содержимое адаптера имеет изменено, но ListView не получил уведомление. Убедитесь, что содержимое вашего адаптера не изменяется из фонового потока, но только из потока пользовательского интерфейса. [в ListView (-1, класс android.widget.ListPopupWindow $DropDownListView) с адаптером (класс com.example.parkfoxxlight_android.PlacesAutoCompleteAdapter)]
Полный журнал: http://pastebin.com/Hx7k28Rm
Полный код адаптера: http://pastebin.com/TfH1bXE3 Я использую пример из https://developers.google.com/places/training/autocomplete-android, и он имеет вполне стандартный код, поэтому кажется, что в коде google есть ошибка?
Приложение вызывается только иногда с указанным выше сообщением об ошибке.
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
Активность 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);
}
}
Есть идеи, как это исправить? Я на Android 4.3.
Ответы
Ответ 1
Метод Filter
performFiltering()
работает в фоновом потоке и из этого метода вы меняете resultList
, на котором основан ваш адаптер. Если вы измените этот список данных, и в это время ListView
получит доступ к адаптеру, он увидит, что что-то изменилось без его ведома (и это не понравится). Вам следует избегать использования resultList
в методе performFiltering
и просто создать новый временный список:
// 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 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();
}
}
Ответ 2
Попробуйте это (просто догадка):
@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;
}