问题描述
我目前正在编写的应用程序处理地点.创建地点的主要方法是输入地址.提供一种方便的方式来完成这件事对用户体验来说非常重要.
The app I am currently writing deals with places. The main method to create a place is to enter an address. Providing a convenient way to do it is very important for the UX.
在对该问题进行了一段时间的调查后,我得出的结论是,最好的方法是使用基于当前位置和用户输入(如在 Google 地图应用程序中)自动完成的文本区域.遗憾的是,由于 Google Map API 未提供此功能,因此我必须自己实现.
After investigating the problem a while I came with the conclusion that the best way to do it is a text area with autocompletion based on the current location and user input (like in the Google Map application). Since this feature is unfortunately not provided by the Google Map API I have to implement it by myself.
为了获得地址建议,我使用 Geocoder.这些建议由自定义 AutoCompleteTextView 提供http://developer.android.com/reference/android/widget/ArrayAdapter.html" rel="nofollow">ArrayAdaptor 通过自定义 过滤器.
To get address suggestions I use Geocoder. These suggestions are provided to an AutoCompleteTextView by a custom ArrayAdaptor through a custom Filter.
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;
}
...
问题
这个实现确实有效,但并不完美.
Problems
This implementation does work, but isn't perfect.
AddressLoader 有一些输入的奇怪行为.
例如如果用户想输入这个地址
The AddressLoader have a strange behavior with some input.
For example if the user want to enter this address
10 rue Guy Ropartz 54600 Villers-les-Nancy
当他进入时:
10 rue Guy
有一些建议,但没有想要的地址.
如果他在达到此输入时继续输入文本:
there are some suggestions but not the desired address.
If the he continue entering text when this input is reached:
10 rue Guy Ro
建议消失.输入为
10 rue Guy Ropart
我认为这让用户感到不安.奇怪的是,没有关于 "10 rue Guy Ro" 的建议,而有关于 "10 rue Guy" 和 "10 rue Guy Ropart"的建议em>...
I think this is disturbing for the user. it's strange that there is no suggestion for "10 rue Guy Ro" while there are suggestions for "10 rue Guy" and "10 rue Guy Ropart"...
我想了一个可能的解决方法来解决这个问题并尝试实施它.这个想法是如果在输入更长的地址后 AdressLoader 没有建议的地址,则保留先前的建议.在我们的示例中,当输入为 "10 rue Guy Ro" 时,保留 "10 rue Guy" 建议.
I imagine a possible workaround for this issue and try to implement it. The idea is to keep previous suggestion if after typing a longer address there are no address suggested by AdressLoader. In our exemple keep "10 rue Guy" suggestions when the input is "10 rue Guy Ro".
不幸的是,我的代码不起作用.当我处于这种情况时,代码的大部分内容都已到达:
Unfortunately my code doesn't work. When I am in this situation the good portion of code is reached :
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();
}
并且 performFiltering 返回的 FilterResult 充满了好的建议,但它没有出现在视图中.也许我错过了 publishResults...
and the FilterResult returned by performFiltering is filled with the good suggestions but it doesn't appear on the view. Maybe I miss something in publishResults...
- 地理编码器有时会出现奇怪的行为,也许有更好的方法来获取地址建议?
- 你能推荐一种比我描述的更好的解决方法吗?
- 在实施我的解决方法时有什么问题?
更新
出于兼容性问题,我基于 Google Geocode Http API 实现了一个新的 AddressLoader(因为默认的 Android 地理编码器服务并非在所有设备上都可用).它重现了完全相同的奇怪行为.
Update
For compatibility concerns, I implemented a new AddressLoader based on the Google Geocode Http API (because the default Android geocoder service is not available on all devices). It reproduce the exact same strange behavior.
推荐答案
Geocoder api 并非旨在为诸如 "10 rue Guy Ro" 之类的部分字符串提供建议.你可以在谷歌地图上试试这个,输入10 rue Guy Ro"并留一个空格.你不会得到任何建议(谷歌地图正在使用地理编码器)但是当你删除空间时,你会得到关于部分字符串的建议(谷歌地图正在使用私有 API).您可以像使用 Google 地图一样使用 Geocoder api,仅在用户输入的字符串中输入空格后获取建议.
The Geocoder api is not designed to give suggestions on partial strings like "10 rue Guy Ro" . You can try this in Google maps, enter "10 rue Guy Ro" and leave a space. You will not get any suggestions(Google maps is using Geocoder) but when you remove the space you will get suggestions on the partial string (Google Maps is using private API).You can use the Geocoder api the same way Google maps use, fetch the suggestion only after the user input space in the string he is typing.
这篇关于使用 ArrayAdapter 和 Filter 的 Android AutocompleteTextView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!