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();
        }
    }
}
4

3 回答 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;
    }
}
于 2014-03-10T16:31:19.120 回答
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();
        }
    }
于 2012-11-08T03:27:04.263 回答
0

AutoCompleteTextView.setAdapter()方法接受继承ListAdapterFilterable接口的类的实例。 Filterable界面是您问题的关键。

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

如果您PlaceAdapter从那里继承,ArrayAdapter您应该注意到ArrayAdapter已经实现了Filterable接口和getFilter()方法返回ArrayFilterperformFiltering(CharSequence prefix)方法中获取当前项ArrayAdapter,将它们转换为小写字符串,item.toString().toLowerCase()并仅过滤其中以 开头的单词prefix.toString().toLowerCase()

接下来我注意到如果Filter.performFiltering()方法返回然后下拉列表被隐藏FilterResultsFilterResults.count == 0

因此,如果您没有ArrayAdapter.getFilter()使用执行您的自定义过滤器覆盖方法,SearchPlacesTask那么您最终会隐藏下拉列表,因为Filter.performFiltering()返回零结果并显示何时SearchPlacesTask.onPostExecute()分配新适配器和调用notifyDataSetChanged()

最好的选择是不要在AsyncTask内部使用和调用您的 API 方法Filter.performFiltering()

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那么您可以像同步执行SearchPlacesTask一样等待任务完成并从方法返回结果。Filter.performFiltering()new SearchPlacesTask().execute(query).get()Filter.performFiltering()

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

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

AutoCompleteTextView当用户更快地更改文本然后加载新结果并取消以前的请求时管理重复请求,因此Filterable在您的适配器中实现是正确使用的唯一正确方法AutoCompleteTextView

于 2015-07-19T16:33:52.527 回答