安卓中的地理编码自动完成

15

我已经费尽心思在谷歌上搜索,试图找到与我遇到相同问题的人,但没有成功。以下是我的问题:

我正在尝试使用Android中的地理编码器实现地址自动完成建议,当用户键入地方的名称时,我希望它的行为方式和JavaScript版本使用组合框(combbox)大致相同。

我使用了一个带有AutoCompleteTextView的布局,并使用ArrayAdapter动态更新建议列表,当用户键入时添加了一个500ms的延迟,在onTextChanged()事件被接收后调用geocoder.getFromLocationName 使用Handler。如果用户在500ms内键入更多字母,则最后一个事件将被取消。我遇到的问题是几乎从不在UI中作为可选择项显示建议。我得到了地址建议,但当我将它们添加到附加到autocomplatetextview的适配器中时,它们就不会显示。

我在使用包括Google API的API级别7的模拟器上运行此代码。

现在提供一些源代码来帮助你:

<LinearLayout android:id="@+id/searchInputLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="6dip"
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/searchMessage" />
    <EditText android:id="@+id/freetextInput" 
        android:hint="@string/searchFreetextLabel"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:background="@android:drawable/editbox_background" />
    <CheckBox android:id="@+id/includeVincinityCheckbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/includeVincinityLabel"
        android:checked="true"
        android:onClick="includeVincinityClick" />
    <AutoCompleteTextView android:id="@+id/locationInput" 
        android:hint="@string/locationInputHint"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" />
    <Button android:id="@+id/searchButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/searchBtnLabel" 
        android:onClick="searchBtnClicked" />
    </LinearLayout>

我的活动源代码(省略了不相关的代码):

public class SearchLocationTabActivity extends Activity implements TextWatcher, OnItemSelectedListener {

private static final int MESSAGE_TEXT_CHANGED = 0;
private static final int AUTOCOMPLETE_DELAY = 500;
private static final int THRESHOLD = 3;
private String latitude, longitude;
private List<Address> autoCompleteSuggestionAddresses;
private ArrayAdapter<String> autoCompleteAdapter;
private Handler messageHandler;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.search);
    setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);

    messageHandler = new MyMessageHandler(this, this);
    autoCompleteAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, new ArrayList<String>());
    autoCompleteAdapter.setNotifyOnChange(false);
    AutoCompleteTextView locationinput = (AutoCompleteTextView) findViewById(R.id.locationInput);
    locationinput.addTextChangedListener(this);
    locationinput.setOnItemSelectedListener(this);
    locationinput.setThreshold(THRESHOLD);
    locationinput.setAdapter(autoCompleteAdapter);
}

@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    messageHandler.removeMessages(MESSAGE_TEXT_CHANGED);
}

@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
String value = arg0.toString();
if (!"".equals(value) && value.length() >= THRESHOLD) {
    Message msg = Message.obtain(messageHandler, MESSAGE_TEXT_CHANGED, arg0.toString());
    messageHandler.sendMessageDelayed(msg, AUTOCOMPLETE_DELAY);
} else {
    autoCompleteAdapter.clear();
}
}

@Override
public void afterTextChanged(Editable arg0) {
}

@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
    if (arg2 < autoCompleteSuggestionAddresses.size()) {
        Address selected = autoCompleteSuggestionAddresses.get(arg2);
        latitude = Double.toString(selected.getLatitude());
        longitude = Double.toString(selected.getLongitude());
    }
}

private void notifyResult(List<Address> suggestions) {
    latitude = longitude = null;
    autoCompleteAdapter.clear();
    for (Address a : autoCompleteSuggestionAddresses) {
        autoCompleteAdapter.add(a.toString());//TODO: figure out a nice way to display this address in list
    }
    autoCompleteAdapter.notifyDataSetChanged();
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
    latitude = longitude = null;
}

private class MyMessageHandler extends Handler {

    private Context context;
    private AsyncTaskSubscriber subscriber;

    public MyMessageHandler(Context context, AsyncTaskSubscriber subscriber) {
        this.context = context;
        this.subscriber = subscriber;
    }

    @Override
    public void handleMessage(Message msg) {
        if (msg.what == MESSAGE_TEXT_CHANGED) {
            String enteredText = (String) msg.obj;

            try {
                autoCompleteSuggestionAddresses = new Geocoder(context).getFromLocationName(enteredText, 10);

                notifyResult(response);
            } catch (IOException ex) {
                Log.e(GeoCoderAsyncTask.class.getName(), "Failed to get autocomplete suggestions", ex);
            }
        }
    }
}
}

非常感谢任何帮助!


我也遇到了同样的问题。你最终是怎么解决的? - jayearn
我已经取消了AutocompleteTextView上的过滤。请参见下面解释的解决方案 ;) - Runar Halse
3个回答

3

对于那些没有成功去除过滤的人,这是我所做的(还有其他小修改,但我认为它们对过滤部分没有影响)。还要注意,要检测到对项目的单击,您需要添加OnItemClickListener

autoCompleteAdapter = new ArrayAdapterNoFilter(this, android.R.layout.simple_dropdown_item_1line);

ArrayAdapterNoFilter的灵感来源于这个答案

public class ArrayAdapterNoFilter extends ArrayAdapter<String> {

    public ArrayAdapterNoFilter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
    }

    private static final NoFilter NO_FILTER = new NoFilter();

    /**
     * Override ArrayAdapter.getFilter() to return our own filtering.
     */
    @Override
    public Filter getFilter() {
        return NO_FILTER;
    }

    /**
     * Class which does not perform any filtering. Filtering is already done by
     * the web service when asking for the list, so there is no need to do any
     * more as well. This way, ArrayAdapter.mOriginalValues is not used when
     * calling e.g. ArrayAdapter.add(), but instead ArrayAdapter.mObjects is
     * updated directly and methods like getCount() return the expected result.
     */
    private static class NoFilter extends Filter {
        protected FilterResults performFiltering(CharSequence prefix) {
            return new FilterResults();
        }

        protected void publishResults(CharSequence constraint, FilterResults results) {
            // Do nothing
        }
    }
}

谢谢你的支持!它节省了我大量的时间! - Pankaj

1

好的,这个问题有一个非常简单的解决方案。AutoCompleteTextView组件中的过滤机制导致结果并不总是显示出来。因为地理编码器返回的结果并不一定包含用户输入的字符串,所以它没有显示那些结果。


2
@Runar:您能否分享一下如何删除autocompletetextview中的过滤器,因为它没有公开的方法来执行此操作。如果您能够友好地分享AsyncTaskSubscriber,那么这是您自定义的类吗? - Nohsib
@Nohsib 如果你还需要的话,我已经添加了一个答案来展示如何移除过滤器。 - assylias

0

这对我的地理编码代码实际上很有效。我可以将文本监视器添加到自动完成文本视图中,然后获取文本并运行异步任务,从谷歌的GeoCode类中获取地址列表。使用上面的无过滤数组适配器使其在我正确输入字母时显示地址。

谢谢!


我指的是无过滤器数组适配器。 - Danuofr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接