検索可能なアクティビティを作成しました。ここで、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 リクエストを変更できる場合は、クエリ メソッド内で単にブロッキングを呼び出すことができます。不要なリクエストを停止するために、割り込みやその他のシグナル (カスタムのものかもしれません) をリッスンするのに役立つ場合があります。
もう 1 つのオプション - 既に非同期になっているリクエスト クラスを変更したくない場合 (Robospice を使用している場合など) は、MatrixCursor 参照を返して後で入力する必要があります。AbstractCursor クラスはすでに Observer パターンを実装しており、変更があった場合に通知を送信します。検索システムがリッスンしている場合、データの変更を処理する必要があります。私はまだそれを自分で実装していないので、私が描いたようにうまくいくかどうかは確認できません. (さらなるインスピレーションについては、CursorLoader のソースをご覧ください。)
とにかく、それはカーソルのポイント全体ではありませんか? それ以外の場合は、データを含むリストを単純に返すことができます。
更新: 私にとっては、MatrixCursor を使用してもうまくいきませんでした。代わりに、他の 2 つのソリューションを実装しました。
- AutoCompleteTextField を、Filter のカスタム サブクラスを使用する ArrayAdapter のカスタム サブクラスと組み合わせて使用します。メソッド
Filter#performFiltering()
(リモート サービスへの同期呼び出しでオーバーライドします) は非同期で呼び出され、UI スレッドはブロックされません。 SearchableActivity およびカスタム ArrayAdapter (カスタム フィルターなし) で SearchWidget を使用する。検索インテントが来ると、リモート リクエストが開始され (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); } } }
このメソッドは、アダプターで通知をトリガーし、検索結果リストをトリガーします。
これを解決するために私が見つけた最も近い方法は、ContentProviderを使用し、Query
プロバイダーのメソッドでネットワークリクエストを実行することです(これがベストプラクティスに反する場合でも)。その結果、こことここMatrixCursor
に示すようにを作成できます。 。
私はまだ他のSyncAdapter
場所で使用されていない提案を表示するためだけに圧倒されるように見えるを使用するような他のオプションを探しています。
これを非同期で行うために取ったもう1つのオプションは、AutoCompleteTextViewを使用することです。これにより、この回答getFilter
に示されている関数を実装できるカスタムアダプターを作成できます。