Android:如何从网上异步获取搜索建议?

时间:2012-07-23 10:50:22

标签: android search search-suggestion

我创建了一个可搜索的活动。现在,我想添加从Web服务中获取的搜索建议。我想异步获得这些建议。根据{{​​3}}我需要覆盖查询方法,执行我的建议搜索,构建我自己的Adding Custom Suggestions并返回它。但这是问题所在,我提出建议的要求是异步的。所以当结果从net返回时它在查询方法的范围之外。

3 个答案:

答案 0 :(得分:3)

以下是SearchView的示例,其中包含来自网络服务的建议(我使用了Retrofit):

@Override
public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_search_activity, menu);

        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.search));

        final CursorAdapter suggestionAdapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_1,
                null,
                new String[]{SearchManager.SUGGEST_COLUMN_TEXT_1},
                new int[]{android.R.id.text1},
                0);
        final List<String> suggestions = new ArrayList<>();

        searchView.setSuggestionsAdapter(suggestionAdapter);
        searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
            @Override
            public boolean onSuggestionSelect(int position) {
                return false;
            }

            @Override
            public boolean onSuggestionClick(int position) {
                searchView.setQuery(suggestions.get(position), false);
                searchView.clearFocus();
                doSearch(suggestions.get(position));
                return true;
            }
        });

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {

                MyApp.autocompleteService.search(newText, new Callback<Autocomplete>() {
                    @Override
                    public void success(Autocomplete autocomplete, Response response) {
                        suggestions.clear();
                        suggestions.addAll(autocomplete.suggestions);

                        String[] columns = {
                                BaseColumns._ID,
                                SearchManager.SUGGEST_COLUMN_TEXT_1,
                                SearchManager.SUGGEST_COLUMN_INTENT_DATA
                        };

                        MatrixCursor cursor = new MatrixCursor(columns);

                        for (int i = 0; i < autocomplete.suggestions.size(); i++) {
                            String[] tmp = {Integer.toString(i), autocomplete.suggestions.get(i), autocomplete.suggestions.get(i)};
                            cursor.addRow(tmp);
                        }
                        suggestionAdapter.swapCursor(cursor);
                    }

                    @Override
                    public void failure(RetrofitError error) {
                        Toast.makeText(SearchFoodActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
                        Log.w("autocompleteService", error.getMessage());
                    }
                });
                return false;
            }
        });

        return true;
}

答案 1 :(得分:1)

根据这个答案,似乎无法在UI线程上运行对建议内容提供者的请求:https://stackoverflow.com/a/12895381/621690。 如果您可以更改您的http请求,您可以简单地在查询方法中将其称为阻止。可能有助于监听中断或其他信号(可能是自定义信号)以阻止不必要的请求。

另一个选项 - 如果您不想更改任何已经异步的请求类(如果您使用的是Robospice) - 应该只返回MatrixCursor引用并稍后填充它。 AbstractCursor类已经实现了Observer模式,并在发生更改时发送通知。如果搜索系统正在侦听,它应该处理数据中的任何更改。我还没有自己实现,所以我不能确认它会像我想象的那样好用。 (看看CursorLoader的来源,获得更多灵感。)

而且,无论如何,这不是游标的全部意义吗?否则我们只需返回一个包含数据的列表。

更新: 对我来说,使用MatrixCursor并没有成功。相反,我已经实施了另外两个解决方案:

  1. 将AutoCompleteTextField与ArrayAdapter的自定义子类结合使用,ArrayAdapter本身使用Filter的自定义子类。方法Filter#performFiltering()(我使用对远程服务的同步调用覆盖)是异步调用的,并且不阻止UI线程。
  2. 将SearchWidget与SearchableActivity和自定义ArrayAdapter一起使用(不使用自定义过滤器)。当搜索意图进入时,启动远程请求(Robospice),当它通过回调返回时,我在我的ArrayAdapter<Tag>子类上调用以下自定义方法:

    public void addTags(List<Tag> items) {
        if (items != null && items.size() > 0) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                super.setNotifyOnChange(false);
                for (Tag tag : items) {
                    super.add(tag);
                }
                super.notifyDataSetChanged();
            } else {
                super.addAll(items);
            }
        }
    }
    
  3. 此方法负责触发适配器上的通知,从而触发搜索结果列表。

答案 2 :(得分:0)

我发现最接近解决此问题的方法是使用ContentProvider并在提供商的Query方法上执行网络请求(即使这违反了最佳做法),结果你可以创建一个MatrixCursor,因为它显示为herehere

我仍然在寻找其他选项,例如使用SyncAdapter,这似乎是为了显示其他地方没有使用过的建议而压倒性的。

我异步执行此操作的另一个选项是使用AutoCompleteTextView,这样您就可以创建自定义适配器,您可以在其中实现this answer中显示的getFilter函数。