AutoCompleteTextView在适配器更改时隐藏和显示下拉列表

4
我有一个AutoCompleteTextView,通过AsyncTask从API获取建议。 在onPostExecute中,我创建了一个新的适配器,将其设置为AutoCompleteTextView,并通知适配器数据集发生了变化。
在TextWatcher中,我执行AsyncTask。
除了每次更改适配器时都会关闭并显示下拉菜单之外,一切正常。
如何在数据更改时保持下拉菜单处于打开状态?
searchText.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (s.length() > 0) {
                searchPlacesTask.cancel(true);
                searchPlacesTask = new SearchPlacesTask();
                searchPlacesTask.execute(s.toString().replace(" ", "-"));
            } else {
                searchPlacesTask.cancel(true);
                searchPlacesTask = new SearchPlacesTask();
                searchPlacesTask.execute();
            }
        }
    });



private class SearchPlacesTask extends AsyncTask<String, Void, PlacesAPIResult> {

    @Override
    protected PlacesAPIResult doInBackground(String... params) {
        PlacesAPIResult result = new PlacesAPIResult();

        if (params.length > 0) {
            places = PlacesAPIRestMethod.placeAutocomplete(params[0], currentLocation.getLatitude(), currentLocation.getLongitude(), 
                    500, null, result);
        } else {
            places = PlacesAPIRestMethod.placeSearch(currentLocation.getLatitude(), currentLocation.getLongitude(), 0, true, result);
        }

        return result;
    }

    @Override
    protected void onPostExecute(PlacesAPIResult result) {
        if (result.getResult() == PlacesAPIResult.OK) {
            adapter = new PlaceAdapter(ChooseLocationActivity.this, R.layout.locationrow, places);
            searchText.setAdapter(adapter);
            adapter.notifyDataSetChanged();
        }
    }
}

你好,我遇到了同样的问题,但是无法解决。如果你能够修复它,请告诉我。 - Zoombie
现在我也遇到了同样的问题。如果你有解决方案,请告诉我。 - Huy Duong Tu
3个回答

4

在适配器中实现Filterable,并添加以下代码。

public class ListAdapter extends ArrayAdapter<String> implements Filterable{


    private List<String> listResult;

    ...
    @Override
    public Filter getFilter() {
        Filter filter = new Filter() {

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults filterResults = new FilterResults();
                if (constraint != null) {
                    // Assign the data to the FilterResults
                    filterResults.values = listResult;
                    filterResults.count = listResult.size();
                    }
                return filterResults;
                }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null && results.count > 0) {
                    notifyDataSetChanged();
                }
                else {
                    notifyDataSetInvalidated();
                }
            }};
        return filter;
    }
}

我简直不敢相信这个起作用了...虽然我不太明白,但你刚刚帮我省了几个小时的额外工作,非常感谢!就记录而言,每当我想要刷新适配器时,我都会给listResult分配值。 - Analizer
这是如何工作的?它如何作为下拉菜单展开?您能否解释一下?另外,如何获取下拉菜单的itemclick事件? - KarthikKPN

1

我曾经遇到过同样的问题,我的解决方法如下:

  1. 创建一个外部类,继承ArrayAdapter并实现TextWatcher,将一个空数据集合传递给它。
  2. 像你一样启动工作线程。
  3. 实现getFilter()方法。这里没有什么重要的操作,只是一个带有notifyDatachanged()的存根。
  4. onProgressUpdate中更新适配器的数据。在onPostExecute中也应该可以工作。我的问题就出在这里。起初,我完全清除了旧数据,然后才添加新数据。这导致了下拉列表项的“隐藏和显示”效果。然后我改变了清除旧数据的方式——我逐个删除旧数据并逐个添加新数据。这样就解决了不必要的效果。

对于你的情况,我建议将PlaceAdapter的构造和设置适配器的操作从onPostExecute中移出,并进行以下操作:

@Override
    protected void onPostExecute(PlacesAPIResult result) {
        if (result.getResult() == PlacesAPIResult.OK) {
            //TODO: iterate adapter's data and remove every item
            //TODO: iterate `places` collection and add each item to adapter's data
            adapter.notifyDataSetChanged();
        }
    }

0

AutoCompleteTextView.setAdapter() 方法接受继承了 ListAdapterFilterable 接口的类的实例。

Filterable 接口是解决问题的关键。

当您在 AutoCompleteTextView 中输入文本时,它使用从 Filterable.getFilter() 方法返回的 Filter 来过滤下拉列表中的项目。

如果您的 PlaceAdapter 继承自 ArrayAdapter,那么您应该注意到 ArrayAdapter 已经实现了 Filterable 接口,并且 getFilter() 方法返回 ArrayFilter,在 performFiltering(CharSequence prefix) 方法中,它将 ArrayAdapter 的当前项目转换为小写字符串,并仅过滤其中以 prefix.toString().toLowerCase() 开头的单词。

接下来我注意到,如果 Filter.performFiltering() 方法返回带有 FilterResults.count == 0FilterResults,则下拉列表会隐藏。

如果您没有使用自定义过滤器覆盖ArrayAdapter.getFilter()方法并执行您的SearchPlacesTask,那么由于Filter.performFiltering()返回零结果而导致下拉列表隐藏,并在SearchPlacesTask.onPostExecute()分配新适配器并调用notifyDataSetChanged()时显示。

最好的选择是不使用AsyncTask,而是直接在Filter.performFiltering()内调用API方法:

public class PlacesAdapter extends ArrayAdapter<String> {
    private final List<Place> items = new ArrayList<Place>();

    // other code
    // ...

    @Override
    public Filter getFilter() {
        return new Filter() {

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults filterResults = new FilterResults();
                String query = constraint != null ? constraint.toString() : "";
                List<Place> places;
                PlacesAPIResult result = new PlacesAPIResult();
                if (query.isEmpty()) {
                    places = PlacesAPIRestMethod
                        .placeSearch(currentLocation.getLatitude(), 
                            currentLocation.getLongitude(), 0, true, result);

                } else {
                    places = PlacesAPIRestMethod.placeAutocomplete(query, 
                        currentLocation.getLatitude(), currentLocation.getLongitude(), 
                        500, null, result);
                }
                if (result.getResult() == PlacesAPIResult.OK) {
                    filterResults.values = places;
                    filterResults.count = places.size();
                }
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                List<Place> places = (List<Place>) results.values;
                if (places != null) {
                    items.clear();
                    items.addAll(places);
                    notifyDataSetChanged();
                }
                else {
                    notifyDataSetInvalidated();
                }
            }
        };
    }
}

如果你被迫使用 AsyncTask,那么你可以在 Filter.performFiltering() 中执行 SearchPlacesTask,像这样:new SearchPlacesTask().execute(query).get(),以同步等待任务完成并从 Filter.performFiltering() 方法返回结果。

Filter.performFiltering() 方法在工作线程中调用,因此您不必担心 API 方法的异步执行。

Filter.publishResults() 方法在 UI 线程中调用,这是您应该更新适配器并调用 notifyDataSetChanged() 的地方。

AutoCompleteTextView 在用户更改文本速度比新结果加载更快时管理重复请求,并取消先前的请求,因此在适配器中实现 Filterable 是正确使用 AutoCompleteTextView 的唯一正确方式。


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