Динамическое обновление адаптера AutoCompleteTextView
Я хочу периодически изменять предложения, представленные AutoCompleteTextview, получая список от веб-службы RESTful, и не могу заставить его работать гладко. Я создал жестко запрограммированный список предложений, чтобы убедиться, что он работает:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, new String[] {"Hi", "Ho"});
speciesName.setAdapter(adapter);//my autocomplete tv
У меня есть TextWatcher для textview, и когда изменяется текст, который запускает неблокирующий вызов для получения нового списка предложений - эта часть, которая получает новый список, работает нормально. Затем я хочу сбросить адаптер, вот так:
public void setOptionsAndUpdate(String[] options) {
Log.d(TAG, "setting options");
//speciesName.setAdapter((ArrayAdapter<String>)null);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, options);
speciesName.setAdapter(adapter);
}
Этот метод вызывается, но не работает - список предложений либо исчезает, либо отображаемые предложения остаются неизменными, несмотря на вызов setAdapter
,
Это даже правильный подход? я смотрел на SimpleCursorAdapter
но не мог понять, как зарегистрировать мой веб-сервис в качестве поставщика контента. (Он имеет вид http://www.blah.com/query?term=XX, где XX - это входные данные из моего приложения, а ответ представляет собой массив строк JSON.)
5 ответов
Вот как я обновляю свой AutoCompleteTextView:
String[] data = terms.toArray(new String[terms.size()]); // terms is a List<String>
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data);
keywordField.setAdapter(adapter); // keywordField is a AutoCompleteTextView
if(terms.size() < 40) keywordField.setThreshold(1);
else keywordField.setThreshold(2);
Теперь, конечно, это статично и не касается беспроводных предложений, но я также могу предложить вам уведомить адаптер об изменениях после того, как вы назначите его AutoCompleteTextView:
adapter.notifyDataSetChanged();
Надеюсь это поможет.
-serkan
Мне не повезло с использованием adapter.notifyDataSetChanged() при динамическом добавлении и изменении данных в адаптере. В моей ситуации я выполнял асинхронный доступ к внешнему API и периодически получал список данных о завершении.
Этот код очищает адаптер и добавляет новые данные, как вы ожидаете. Однако мне пришлось вызвать метод getFilter().Filter, чтобы заставить данные отображаться. Кроме того, мне пришлось явно фильтровать на основе текущего текста в AutocompleteTextView, потому что мой вызов API был асинхронным.
adapter.clear();
for (Map<String, String> map : completions) {
adapter.add(map.get("name"));
}
//Force the adapter to filter itself, necessary to show new data.
//Filter based on the current text because api call is asynchronous.
adapter.getFilter().filter(autocompleteTextView.getText(), null);
По этой теме было довольно хорошее руководство по использованию удаленных данных в API Google Map для заполнения AutoCompleteTextView.
Если вам нужна кэшированная версия, я извлек ее отсюда. Исходный учебник был удален, но по сути вам нужно написать ArrayAdapter с настраиваемым фильтром, аналогичным показанному ниже, и назначить его вашему AutoCompleteTextView.
Примечание. Необходимо реализовать метод autocomplete(), который выполняет любую операцию, необходимую для синхронной выборки и возврата элементов автозаполнения. Поскольку фильтр вызывается в фоновом потоке, это не будет блокировать основной поток пользовательского интерфейса.
private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> resultList;
public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@Override
public int getCount() {
return resultList.size();
}
@Override
public String getItem(int index) {
return resultList.get(index);
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}};
return filter;
}
}
Лучшее решение, которое я нашел для обновления адаптера:
Editable text = autocomplete.getText();
autocomplete.setText(text);
autocomplete.setSelection(text.length());
Как это устроено:
Мы устанавливаем текст autoCompleteTextView с его текущим текстом, поэтому адаптер уведомляет об изменении данных и обновляет содержимое listViews.
Но с помощью этого трюка курсор перемещается в начало текста редактирования. поэтому мы используем autocomplete.setSelection(text.length())
для перемещения курсора до конца.
Работает как шарм!
Редактировать:
Также вы должны использовать clear()
, add()
а также remove()
методы прямо на вашем ArrayAdapter
вместо вашего ArrayList
,
Поскольку я не могу добавить комментарий, я даю новый ответ. Нет необходимости очищать адаптер или вызывать adaptor.getFilter(). Filter (...)... Чтобы динамически обновить адаптер AutoCompleteTextView, просто добавьте новый элемент для адаптера и снова установите adapter. Для примера, приведенного в исходном вопросе, я попробовал следующее, и оно работает (код ниже не показывает начальную настройку адаптера, поскольку здесь приведены несколько ответов. Это просто показывает динамическое обновление адаптера). Обновление адаптера может быть рядом с кодом, который обновляет список, связанный с ArrayAdapter.
adapter.add(String newSuggestion); // this goes inside a loop for adding multiple suggestions
speciesName.setAdapter(adapter) ; // speciesName is an AutoCompleteTextView as given in the original question.