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

Android:如何从web异步获取搜索建议?,android,search,search-suggestion,Android,Search,Search Suggestion,我已经创建了一个可搜索的活动。现在,我想添加来自web服务的搜索建议。我希望异步获取这些建议。根据需要,我需要覆盖查询方法,执行我的建议搜索,构建我自己的并返回它。但这就是问题所在,我获取建议的请求是异步的。所以,当结果从网络返回时,它将超出查询方法的范围 我发现最接近解决这个问题的方法是使用ContentProvider,并使用提供商的Query方法执行网络请求(即使这违反了最佳实践),结果是您可以创建一个MatrixCursor,如图所示 我还在搜索其他选项,比如使用SyncAdapter,

我已经创建了一个可搜索的活动。现在,我想添加来自web服务的搜索建议。我希望异步获取这些建议。根据需要,我需要覆盖查询方法,执行我的建议搜索,构建我自己的并返回它。但这就是问题所在,我获取建议的请求是异步的。所以,当结果从网络返回时,它将超出查询方法的范围

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

我还在搜索其他选项,比如使用
SyncAdapter
,这似乎是为了显示其他地方没有使用过的建议


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

根据以下回答,对建议内容提供程序的请求似乎没有在UI线程上运行:。 如果可以更改http请求,只需在查询方法内部将其称为阻塞。可能有助于侦听中断或其他信号(可能是自定义信号),以停止不必要的请求

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

不管怎样,这不是光标的全部点吗?否则,我们可以简单地返回一个包含数据的列表

更新: 对我来说,使用MatrixCursor是行不通的。相反,我实施了另外两种解决方案:

  • 将AutoCompleteTextField与ArrayAdapter的自定义子类结合使用,ArrayAdapter本身使用了Filter的自定义子类。异步调用方法
    Filter#performFiltering()
    (我通过对远程服务的同步调用覆盖该方法),并且UI线程不会被阻塞
  • 将SearchWidget与SearchableActivity和自定义ArrayAdapter(无自定义筛选器)一起使用。当输入搜索意图时,远程请求启动(Robospice),当它通过回调返回时,我在我的
    ArrayAdapter
    子类上调用以下自定义方法:

    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);
            }
        }
    }
    
    public void addTags(列表项){
    if(items!=null&&items.size()>0){
    if(Build.VERSION.SDK\u INT

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

    下面是一个SearchView示例,其中包含来自网络服务的建议(我使用了改型):

    @覆盖
    公共布尔onCreateOptions菜单(菜单){
    //为菜单充气;这会将项目添加到操作栏(如果存在)。
    getMenuInflater().充气(R.menu.menu\u搜索活动,菜单);
    final SearchView SearchView=(SearchView)MenuItemCompat.getActionView(menu.findItem(R.id.search));
    final CursorAdapter suggestionAdapter=新的SimpleCursorAdapter(此,
    android.R.layout.simple\u list\u item\u 1,
    无效的
    新字符串[]{SearchManager.SUGGEST_COLUMN_TEXT_1},
    新int[]{android.R.id.text1},
    0);
    最终列表建议=新建ArrayList();
    searchView.setSuggestionAdapter(suggestionAdapter);
    searchView.setOnSuggestionListener(新的searchView.OnSuggestionListener(){
    @凌驾
    公共布尔值onSuggestionSelect(整数位置){
    返回false;
    }
    @凌驾
    公共布尔onSuggestionClick(整数位置){
    searchView.setQuery(建议.get(位置),false);
    searchView.clearFocus();
    doSearch(建议.获取(位置));
    返回true;
    }
    });
    searchView.setOnQueryTextListener(新的searchView.OnQueryTextListener(){
    @凌驾
    公共布尔值onQueryTextSubmit(字符串查询){
    返回false;
    }
    @凌驾
    公共布尔onQueryTextChange(字符串newText){
    MyApp.autocompleteService.search(newText,newcallback()){
    @凌驾
    public void成功(自动完成、自动完成、响应){
    建议。清晰();
    建议.addAll(自动完成.建议);
    字符串[]列={
    BaseColumns.\u ID,
    SearchManager.SUGGEST\u COLUMN\u TEXT\u 1,
    SearchManager.SUGGEST\u列\u意向\u数据
    };
    MatrixCursor=新的MatrixCursor(列);
    对于(int i=0;i@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;
    }