Intereting Posts
Изменение значка меню «Переполнение Android» программно Надутый ImageView для установки в GalleryView – это неправильный размер Есть ли способ для push-уведомлений в libGDX (проекты Android и iOS)? Как захватить событие onclick в отключенном EditText в приложении для Android Постоянное распознавание речи, прослушивающее только одно ключевое слово Как определить, на какой платформе работает текущее приложение в LibGDX? Как изменить логотип запуска приложения в Android Studio? Android facebook applicationId не может быть null Прослушивать нажатие и перетаскивание значения и функции соответственно Есть ли способ изменить андроид: значение windowSoftInputMode из класса java? Как отменить тост Как получить плавные данные ориентации в android SearchView в OptionsMenu не полная ширина Сбой на устройствах Samsung для EditText, содержащих ссылки Как преобразовать сообщение facebook created_time в часовой пояс пользователя?

Android AutocompleteTextView с использованием ArrayAdapter и фильтра

Информация о Backgroud

Приложение, которое я сейчас пишу, касается мест. Основной метод создания места – это ввести адрес. Предоставление удобного способа сделать это очень важно для UX.

Текущее решение

Изучив проблему, я пришел с заключением, что лучший способ сделать это – текстовая область с автозаполнением на основе текущего местоположения и ввода пользователя (например, в приложении «Карта Google»). Поскольку эта функция, к сожалению, не предоставляется API Карт Google, я должен реализовать ее самостоятельно.

Текущая реализация

Чтобы получить адресные предложения, я использую Geocoder . Эти предложения предоставляются AutoCompleteTextView с помощью настраиваемого ArrayAdaptor через настраиваемый фильтр .

ArrayAdapter (Некоторый код удален)

public class AddressAutoCompleteAdapter extends ArrayAdapter<Address> { @Inject private LayoutInflater layoutInflater; private ArrayList<Address> suggested; private ArrayList<Address> lastSuggested; private String lastInput = null; private AddressLoader addrLoader; public AddressAutoCompleteAdapter(Activity activity, AddressLoaderFactory addrLoaderFact, int maxSuggestionsCount) { super(activity,R.layout.autocomplete_address_item); suggested = new ArrayList<Address>(); lastSuggested = new ArrayList<Address>(); addrLoader = addrLoaderFact.create(10); } ... @Override public Filter getFilter() { Filter myFilter = new Filter() { ... @SuppressWarnings("unchecked") @Override protected FilterResults performFiltering(CharSequence constraint) { Log.d("MyPlaces","performFiltring call"); FilterResults filterResults = new FilterResults(); boolean debug = false; if(constraint != null) { // Load address try { suggested = addrLoader.load(constraint.toString()); } catch(IOException e) { e.printStackTrace(); Log.d("MyPlaces","No data conection avilable"); /* ToDo : put something useful here */ } // If the address loader returns some results if (suggested.size() > 0) { filterResults.values = suggested; filterResults.count = suggested.size(); // If there are no result with the given input we check last input and result. // If the new input is more accurate than the previous, display result for the // previous one. } else if (constraint.toString().contains(lastInput)) { debug = true; Log.d("MyPlaces","Keep last suggestion : " + lastSuggested.get(0).toString()); filterResults.values = lastSuggested; filterResults.count = lastSuggested.size(); } else { filterResults.values = new ArrayList<Address>(); filterResults.count = 0; } if ( filterResults.count > 0) { lastSuggested = (ArrayList<Address>) filterResults.values; } lastInput = constraint.toString(); } if (debug) { Log.d("MyPlaces","filter result count :" + filterResults.count); } return filterResults; } @Override protected void publishResults(CharSequence contraint, FilterResults results) { AddressAutoCompleteAdapter.this.notifyDataSetChanged(); } }; return myFilter; } ... 

AddressLoader (некоторый код удален)

 public class GeocoderAddressLoader implements AddressLoader { private Application app; private int maxSuggestionsCount; private LocationManager locationManager; @Inject public GeocoderAddressLoader( Application app, LocationManager locationManager, @Assisted int maxSuggestionsCount){ this.maxSuggestionsCount = maxSuggestionsCount; this.app = app; this.locationManager = locationManager; } /** * Take a string representing an address or a place and return a list of corresponding * addresses * @param addrString A String representing an address or place * @return List of Address corresponding to the given address description * @throws IOException If Geocoder API cannot be called */ public ArrayList<Address> load(String addrString) throws IOException { //Try to filter with the current location Location current = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); Geocoder geocoder = new Geocoder(app,Locale.getDefault()); ArrayList<Address> local = new ArrayList<Address>(); if (current != null) { local = (ArrayList<Address>) geocoder.getFromLocationName( addrString, maxSuggestionsCount, current.getLatitude()-0.1, current.getLongitude()-0.1, current.getLatitude()+0.1, current.getLongitude()+0.1); } if (local.size() < maxSuggestionsCount) { ArrayList<Address> global = (ArrayList<Address>) geocoder.getFromLocationName( addrString, maxSuggestionsCount - local.size()); for (Address globalAddr : global) { if (!containsAddress(local,globalAddr)) local.add(globalAddr); } } return local; } ... 

Проблемы

Эта реализация действительно работает, но не идеальна.

У AddressLoader странное поведение с некоторым вводом.
Например, если пользователь хочет ввести этот адрес

 10 rue Guy Ropartz 54600 Villers-les-Nancy 

Когда он вступает:

 10 rue Guy 

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

 10 rue Guy Ro 

Предложения исчезают. Желаемый адрес появляется, когда вход

 10 rue Guy Ropart 

Я думаю, что это беспокоит пользователя. Странно, что нет предложения «10 rue Guy Ro», в то время как есть предложения для «10 rue Guy» и «10 rue Guy Ropart»

Возможное обходное решение

Я представляю себе возможное обходное решение этой проблемы и пытаюсь ее реализовать. Идея состоит в том, чтобы сохранить предыдущее предложение, если после ввода более длинного адреса нет адреса, предложенного AdressLoader. В нашем примере держите предложения «10 rue Guy», когда вводят «10 rue Guy Ro» .

К сожалению, мой код не работает. Когда я нахожусь в этой ситуации, достигается хорошая часть кода:

 else if (constraint.toString().contains(lastInput)) { debug = true; Log.d("MyPlaces","Keep last suggestion : " + lastSuggested.get(0).toString()); filterResults.values = lastSuggested; filterResults.count = lastSuggested.size(); } 

И FilterResult, возвращаемый функцией performFiltering , заполнен хорошими предложениями, но не отображается в представлении. Возможно, я что-то пропустил в publishResults

Вопросов

  • Геокодер когда-то странное поведение, может быть, есть лучший подход для получения предложений по адресам?
  • Можете ли вы предложить мне лучшее обходное решение, которое я описал?
  • В чем проблема при реализации моего обхода?

Обновить
Что касается проблем совместимости, я реализовал новый AddressLoader на основе API-интерфейсов Google Geocode Http (поскольку служба геокодирования Android по умолчанию недоступна на всех устройствах). Он воспроизводит то же самое странное поведение.

Solutions Collecting From Web of "Android AutocompleteTextView с использованием ArrayAdapter и фильтра"

Geocoder api не предназначен для предложений о частичных строках, таких как «10 rue Guy Ro». Вы можете попробовать это на картах Google, ввести «10 rue Guy Ro» и оставить пространство. Вы не получите никаких предложений (карты Google используют Geocoder), но при удалении пространства вы получите предложения по частичной строке (Google Maps использует частный API). Вы можете использовать Geocoder api так же, как использовать карты Google, получить предложение только после ввода пользовательского пространства в строке, которую он вводит.

Попробуйте изменить эту часть кода:

 if ( filterResults.count > 0) { lastSuggested = (ArrayList<Address>) filterResults.values; } lastInput = constraint.toString(); 

В это:

 if ( filterResults.count > 0) { lastSuggested = (ArrayList<Address>) filterResults.values; lastInput = constraint.toString(); } 

Я думаю, что ваша проблема заключается в том, что вы устанавливаете lastS предложено (через filterResults.values), а затем, в следующем проходе, вы предлагаете обновление -> lastSposed будет установлено на то же значение, что и предлагаемое как Java передает переменные по ссылке, если только примитивы ,

Итак, вместо

If (filterResults.count> 0) {lastSposed = (ArrayList) filterResults.values; }

Ты должен попытаться

 if ( filterResults.count > 0) { lastSuggested.clear(); ArrayList<Address> tempList = (ArrayList<Address>) filterResults.values; int i = 0; while (i < tempList.size()){ lastSuggested.add(tempList.get(i)); i += 1; } }