25

バックグラウンド情報

私が現在書いているアプリは場所を扱っています。場所を作成する主な方法は、住所を入力することです。それを行うための便利な方法を提供することは、UXにとって非常に重要です。

現在のソリューション

問題をしばらく調査した後、それを行うための最良の方法は、現在の場所とユーザー入力(Googleマップアプリケーションのように)に基づいてオートコンプリートを使用するテキスト領域であるという結論に達しました。この機能は残念ながらGoogleMapAPIによって提供されていないため、自分で実装する必要があります。

現在の実装

住所の提案を取得するには、Geocoderを使用します。これらの提案は、カスタムFilterを介してカスタムArrayAdaptorによってAutoCompleteTextViewに提供されます。

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

これはユーザーにとって不安なことだと思います。「10rueGuyRopart 」「10rueGuyRopart」の提案があるのに、 「10rueGuyRo」の提案がないのは不思議です...

考えられる回避策

この問題の可能な回避策を想像し、それを実装しようとします。より長いアドレスを入力した後、AdressLoaderによって提案されたアドレスがない場合、以前の提案を保持するという考え方です。この例では、入力が「10 rue Guy Ro」の場合、「 10rueGuy の提案を保持します。

残念ながら、私のコードは機能しません。私がこの状況にあるとき、コードのかなりの部分に到達します:

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ジオコーダーサービスがすべてのデバイスで利用できるわけではないため)。まったく同じ奇妙な振る舞いを再現します。

4

3 に答える 3

3

Geocoder API は、「10 rue Guy Ro」のような部分的な文字列を提案するようには設計されていません。Google マップでこれを試すことができます。「10 rue Guy Ro」と入力し、スペースを空けてください。提案は得られませんが (Google マップは Geocoder を使用しています)、スペースを削除すると、部分的な文字列で提案が得られます (Google マップはプライベート API を使用しています)。Google マップと同じ方法で Geocoder API を使用できます。ユーザーが入力している文字列にスペースを入力した後にのみ候補を取得します。

于 2012-10-09T06:44:05.540 に答える
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();
}
于 2012-06-26T07:48:02.627 に答える
0

あなたの問題は、(filterResults.values を介して) lastSuggested を提案に設定し、次のパスで提案を更新していることだと思います-> Java がプリミティブでない限り参照によって変数を渡すため、lastSuggested は提案と同じ値に設定されます。 .

だから、代わりに

if (filterResults.count > 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;
    }
}
于 2012-07-15T19:11:09.880 に答える