我目前正在撰写的应用与地方有关。创建场所的主要方法是输入地址。提供便捷的方法对于用户体验非常重要。
在调查问题一段时间后,我得出的结论是,最好的方法是根据当前位置和用户输入自动完成的文本区域(如Google Map应用程序中)。由于Google Map API不提供此功能,因此我必须自行实施。
要使用Geocoder获取地址建议。这些建议由自定义AutoCompleteTextView通过自定义ArrayAdaptor提供给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;
}
...
此实现确实有效,但并不完美。
AddressLoader在输入时有一种奇怪的行为 例如,如果用户想要输入此地址
10 rue Guy Ropartz 54600 Villers-les-Nancy
当他进入时:
10 rue Guy
有一些建议,但不是所需的地址 如果他在达到此输入时继续输入文本:
10 rue Guy Ro
建议消失。当输入为
时,最终所需的地址为apear10 rue Guy Ropart
我认为这对用户来说是令人不安的。奇怪的是,对于“10 rue Guy Ro”没有任何建议,而有关于“10 rue Guy”和“10 rue Guy Ropart”的建议 ...
我想象一下这个问题可能的解决方法并尝试实现它。如果在输入更长的地址后没有AdressLoader建议的地址,那么我们的想法就是保留以前的建议。在我们的例子中,当输入“10 rue Guy Ro”时,请保留“10 rue Guy”的建议。
不幸的是我的代码不起作用。当我遇到这种情况时,会达到很好的代码部分:
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 ...
中的内容 更新
出于兼容性考虑,我实施了基于Google Geocode Http API的新AddressLoader(因为默认的Android地理编码器服务并非在所有设备上都可用)。它重现了完全相同的奇怪行为。
答案 0 :(得分:3)
Geocoder api并非旨在提供有关部分字符串的建议,例如&#34; 10 rue Guy Ro&#34; 。您可以在Google地图中尝试此操作,输入&#34; 10 rue Guy Ro&#34;留一个空间。您不会得到任何建议(谷歌地图使用地理编码器),但当您删除空格时,您将获得部分字符串的建议(谷歌地图使用私有API)。 您可以像使用Google地图一样使用Geocoder api,只有在他输入的字符串中的用户输入空格后才能获取建议。
答案 1 :(得分:0)
尝试更改代码的这一部分:
if ( filterResults.count > 0) {
lastSuggested = (ArrayList<Address>) filterResults.values;
}
lastInput = constraint.toString();
进入这个:
if ( filterResults.count > 0) {
lastSuggested = (ArrayList<Address>) filterResults.values;
lastInput = constraint.toString();
}
答案 2 :(得分:0)
我认为您的问题是您将lastSuggested设置为建议(通过filterResults.values)然后,在下一次传递中,您正在更新建议 - &gt; lastSuggested将设置为建议的值,因为Java通过引用传递变量,除非基元。
所以,而不是
if(filterResults.count&gt; 0){ lastSuggested =(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;
}
}