Android Autocompletetextview С Подсказками Веб-Сервиса

Для одного из ваших приложений Android Книга Трекер Я реализовал собственный Автозаполнениетекстового представления с подсказками к названиям книг, которые динамически загружаются из Гугл Книги при вводе названия книги.

Задача перед компонентом была следующая:

  • Загрузка данных должна осуществляться в отдельном потоке, чтобы не блокировать поток UI;
  • Загрузка подсказок должна начинаться только в том случае, если пользователь приостанавливает ввод (чтобы предотвратить отправку нескольких запросов на сервер после каждого введенного символа);
  • Подсказки должны загружаться, если пользователь ввел строку некоторой минимальной длины (нет смысла начинать загрузку данных для строки из двух или трех символов);
  • При отправке запроса на сервер в правой части поля должен отображаться анимированный прогресс, информирующий пользователя о загрузке.

Конечный результат:

Android AutoCompleteTextView с подсказками веб-сервиса



Шаг 1. Реализуйте собственный адаптер для AutoCompleteTextView.

Адаптер для AutoCompleteTextView — это ключевой компонент, в котором загружаются и сохраняются всплывающие подсказки.

BookAutoCompleteAdapter реализует интерфейс Filterable для перехвата пользовательского ввода из AutoCompleteTextView и передачи его в качестве поискового запроса в веб-службу.

Единственный метод интерфейса Filterable — getFilter(), который должен возвращать экземпляр класса Filter, который загружает и публикует данные.

Наследники класса Filter должны реализовать два метода: PerformFiltering(ограничение CharSequence) и publicResults(ограничение CharSequence, результаты Filter.FilterResults).

Метод PerformFiltering будет автоматически вызываться в отдельном потоке, поэтому нет необходимости создавать и запускать новый поток вручную.

Это уже сделано для разработчика в классе Filter. МетодPublishResults вызывается в потоке пользовательского интерфейса для публикации результатов на экране.



BookAutoCompleteAdapter.java
  
  
  
  
   

public class BookAutoCompleteAdapter extends BaseAdapter implements Filterable { private static final int MAX_RESULTS = 10; private final Context mContext; private List<Book> mResults; public BookAutoCompleteAdapter(Context context) { mContext = context; mResults = new ArrayList<Book>(); } @Override public int getCount() { return mResults.size(); } @Override public Book getItem(int index) { return mResults.get(index); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(mContext); convertView = inflater.inflate(R.layout.simple_dropdown_item_2line, parent, false); } Book book = getItem(position); ((TextView) convertView.findViewById(R.id.text1)).

setText(book.getTitle()); ((TextView) convertView.findViewById(R.id.text2)).

setText(book.getAuthor()); return convertView; } @Override public Filter getFilter() { Filter filter = new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults filterResults = new FilterResults(); if (constraint != null) { List<Books> books = findBooks(mContext, constraint.toString()); // Assign the data to the FilterResults filterResults.values = books; filterResults.count = books.size(); } return filterResults; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (results != null && results.count > 0) { mResults = (List<Books>) results.values; notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } }}; return filter; } /** * Returns a search result for the given book title. */ private List<Book> findBooks(String bookTitle) { // GoogleBooksService is a wrapper for the Google Books API GoogleBooksService service = new GoogleBooksService (mContext, MAX_RESULTS); return service.findBooks(bookTitle); } }



Шаг 2. Создайте XML-разметку для строки подсказки.

Когда подсказки загрузятся, отобразится раскрывающийся список с результатами.

Каждая строка состоит из двух элементов: названия книги и имени автора.



simple_dropdown_item_2line.xml


<Эxml version="1.0" encoding="utf-8"?> <TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android " android:layout_width="match_parent " android:layout_height="wrap_content " android:minHeight="Эandroid:attr/listPreferredItemHeight " android:mode="twoLine " android:paddingStart="Эandroid:attr/listPreferredItemPaddingStart " android:paddingEnd="Эandroid:attr/listPreferredItemPaddingEnd "> <TextView android:id="@+id/text1 " android:layout_width="match_parent " android:layout_height="wrap_content " android:layout_marginTop="16dp " android:textAppearance="Эandroid:attr/textAppearanceLargePopupMenu"/ > <TextView android:id="@+id/text2 " android:layout_width="match_parent " android:layout_height="wrap_content " android:layout_below="@id/text1 " android:layout_alignStart="@id/text1 " android:layout_marginBottom="16dp " android:textAppearance="Эandroid:attr/textAppearanceSmall"/ > </TwoLineListItem>



Шаг 3. Добавьте задержку перед отправкой запроса на сервер.

При использовании стандартного AutoCompleteTextView запрос инициируется после каждого введенного символа.

Если пользователь постоянно печатает, предложения, полученные по предыдущему запросу, могут быть неактуальны для каждого последующего символа.

Это создает ненужные и ресурсоемкие вызовы к серверу, вероятность превышения ограничений API, которые может иметь веб-сервис, а также возвращает устаревшие результаты, загруженные для предыдущего состояния строки запроса.

Чтобы избежать описанных выше проблем, необходимо добавить небольшую задержку между вводом персонажа и отправкой запроса на сервер.

Если человек вводит следующий символ во время этой задержки, запрос предыдущей строки отменяется и перемещается вперед на время задержки.

Если за это время пользователь не меняет строку, запрос отправляется на сервер.

Чтобы реализовать описанное выше поведение, вам необходимо создать собственную реализацию AutoCompleteTextView и переопределить метод PerformFiltering(CharSequence text, int keyCode).

Поле mAutoCompleteDelay указывает время в миллисекундах, по истечении которого запрос будет отправлен на сервер, если пользователь не ввел новые символы.



DelayAutoCompleteTextView.java


public class DelayAutoCompleteTextView extends AutoCompleteTextView { private static final int MESSAGE_TEXT_CHANGED = 100; private static final int DEFAULT_AUTOCOMPLETE_DELAY = 750; private int mAutoCompleteDelay = DEFAULT_AUTOCOMPLETE_DELAY; private ProgressBar mLoadingIndicator; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { DelayAutoCompleteTextView.super.performFiltering((CharSequence) msg.obj, msg.arg1); } }; public DelayAutoCompleteTextView(Context context, AttributeSet attrs) { super(context, attrs); } public void setLoadingIndicator(ProgressBar progressBar) { mLoadingIndicator = progressBar; } public void setAutoCompleteDelay(int autoCompleteDelay) { mAutoCompleteDelay = autoCompleteDelay; } @Override protected void performFiltering(CharSequence text, int keyCode) { if (mLoadingIndicator != null) { mLoadingIndicator.setVisibility(View.VISIBLE); } mHandler.removeMessages(MESSAGE_TEXT_CHANGED); mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_TEXT_CHANGED, text), mAutoCompleteDelay); } @Override public void onFilterComplete(int count) { if (mLoadingIndicator != null) { mLoadingIndicator.setVisibility(View.GONE); } super.onFilterComplete(count); } }



Шаг 4. Добавление анимированного прогресса в поле ввода

Очень важно предоставлять обратную связь, пока пользователь печатает. Необходимо показывать анимированный прогресс в поле ввода названия книги.

Прогресс нужен, чтобы сообщить человеку, что подсказки загружаются и скоро отобразятся.

Таким образом, пользователь будет в курсе и сможет дождаться их появления.

Без такой обратной связи человек может даже не осознавать, что в поле могут отображаться подсказки.

Элементы ProgressBar и DelayAutoCompleteTextView должны быть помещены в FrameLayout, а ProgressBar выровнен по правой стороне родительской группы.

Также необходимо изначально скрыть прогресс, установив атрибут android:visibility="gone".



<Эxml version="1.0" encoding="utf-8"?> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp"> <com.melnykov.booktracker.ui.DelayAutoCompleteTextView android:id="@+id/book_title" android:inputType="textCapSentences" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingRight="32dp" android:imeOptions="flagNoExtractUi|actionSearch"/> <ProgressBar android:id="@+id/progress_bar" style="Эandroid:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|right" android:layout_marginRight="16dp" android:visibility="gone"/> </FrameLayout>

ProgressBar подключается к DelayAutoCompleteTextView с помощью метода setLoadingIndicator(представление ProgressBar) последнего.

Видимость элемента хода выполнения устанавливается на View.VISIBLE при загрузке всплывающих подсказок и на View.GONE после завершения загрузки.



Шаг 5 — Соединение компонентов

Теперь, когда все детали готовы, нужно соединить их между собой:

DelayAutoCompleteTextView bookTitle = (DelayAutoCompleteTextView) findViewById(R.id.book_title); bookTitle.setThreshold(4); bookTitle.setAdapter(new BookAutoCompleteAdapter(context)); bookTitle.setLoadingIndicator((ProgressBar) findViewById(R.id.progress_bar)); bookTitle.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { Book book = (Book) adapterView.getItemAtPosition(position); bookTitle.setText(book.getTitle()); } });

bookTitle.setThreshold(4) определяет минимальное количество символов, которое пользователь должен ввести перед отображением всплывающих подсказок.

bookTitle.setLoadingIndicator((ProgressBar) findViewById(R.id.progress_bar)) соединяет ProgressBar с DelayAutoCompleteTextView. Важно установить OnItemClickListener для DelayAutoCompleteTextView и присвоить правильное значение полю ввода.

Если этого не сделать, то в поле вместо названия книги будет вставлен результат вызова метода toString() выбранного объекта.

Теги: #Android #java #AutoCompleteTextView #пользовательский вид #Google Книги #java #Разработка мобильных приложений #Разработка для Android

Вместе с данным постом часто просматривают: