我创建了一个可搜索的活动。现在,我想添加从 Web 服务获取的搜索建议。我想异步获得这些建议。根据添加自定义建议,我需要覆盖查询方法,进行建议搜索,构建自己的MatrixCursor并返回它。但这就是问题所在,我对获得建议的要求是异步的。因此,当结果从网络返回时,它超出了查询方法的范围。
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;
}
无论如何,根据这个答案,对建议内容提供者的请求似乎没有在 UI 线程上运行:https ://stackoverflow.com/a/12895381/621690 。如果您可以更改您的 http 请求,您可以简单地在查询方法中将其称为阻塞。可能有助于监听中断或其他信号(可能是自定义信号)以停止不必要的请求。
另一种选择 - 如果您不想更改任何已经异步的请求类(例如,如果您正在使用 Robospice) - 应该只返回 MatrixCursor 引用并稍后填充它。AbstractCursor 类已经实现了观察者模式,并在发生变化时发出通知。如果搜索系统正在侦听,它应该处理数据中的任何更改。我自己还没有实现它,所以我无法确认它是否会像我想象的那样完美。(查看 CursorLoader 的来源以获得更多灵感。)
而且,无论如何,这不是光标的全部意义吗?否则我们可以简单地返回一个包含数据的列表。
更新:对我来说,使用 MatrixCursor 没有成功。相反,我实施了另外两个解决方案:
- 将 AutoCompleteTextField 与 ArrayAdapter 的自定义子类结合使用,后者本身使用 Filter 的自定义子类。该方法
Filter#performFiltering()
(我用对远程服务的同步调用覆盖)被异步调用,并且 UI 线程没有被阻塞。 将 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); } } }
此方法负责触发适配器上的通知,从而触发搜索结果列表。