【问题标题】:Android: How to get search suggestions asynchronously from the web?Android:如何从网络异步获取搜索建议?
【发布时间】:2012-07-21 14:26:59
【问题描述】:

我创建了一个可搜索的活动。现在,我想添加从 Web 服务获取的搜索建议。我想异步获得这些建议。根据Adding Custom Suggestions,我需要重写查询方法,进行我的建议搜索,构建我自己的MatrixCursor 并返回它。但这就是问题所在,我对获得建议的要求是异步的。因此,当结果从网络返回时,它超出了查询方法的范围。

【问题讨论】:

  • 搜索建议是指 AutoCompleteTextView??
  • 没有。我正在使用 Android 搜索对话框。如文档中所述:“使用 Android 搜索对话框或搜索小部件时,您可以提供根据应用程序中的数据创建的自定义搜索建议。”我想做的是从服务器而不是从数据库中获取这些建议。

标签: android search search-suggestion


【解决方案1】:

这是一个 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;
}

【讨论】:

  • 你能更好地解释一下你的实现吗?比如,doSearch(suggestions.get(position)) 的方法是做什么的?谢谢
  • 为我工作。 doSearch 方法只是在用户单击搜索建议时执行您想要执行的任何操作的方法
【解决方案2】:

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

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

而且,无论如何,这不是光标的全部意义吗?否则我们可以简单地返回一个包含数据的列表。

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

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

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

此方法负责触发适配器上的通知,从而触发搜索结果列表。

【讨论】:

  • 绝对是正确的想法。我正在尝试这样做,但看起来 Android 的搜索建议不会监听光标的变化。 :/ 具体来说,我正在调用ContentResolver.notifyChange(),但我没有看到Android 的SuggestionAdapter 调用registerContentObserver()。有任何想法吗?我错过了什么吗?
  • 这听起来像是一个正确的想法,但它没有成功(至少对我来说不是)。用我当前的解决方案更新了我的答案。
  • 感谢更新!这些当前的解决方案对搜索结果有意义。不过,我仍在寻找在search suggestions 被退回后更新它们的方法。我怀疑可能没有办法......但如果你知道,请告诉我们!
【解决方案3】:

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

我仍在寻找其他选项,例如使用 SyncAdapter,这对于仅显示其他地方未使用的建议而言似乎是压倒性的。

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

【讨论】:

  • 创建示例中显示的 MatrixCursor 并不能解决问题,因为我想从服务器异步获取建议。
  • @toko 请查看我的答案编辑,我添加了另一个选项,这是我最后采用的选项,当您从网络获取数据时不会阻塞您的 UI 线程。
  • 问题是我没有使用 AutoCompleteTextView。我正在使用搜索界面对话框:developer.android.com/guide/topics/search/search-dialog.html
  • @toko 好吧,如果你必须坚持使用搜索对话框,你需要对服务器进行调用而不是异步,将你的网络请求放在你的 Provider 的 Query 函数中......违反指南,尽管在这种情况下您可能没有其他选择。
猜你喜欢
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多