i++

プログラム系のメモ書きなど

Android : SearchView に独自の検索候補を表示する

Adding Custom Suggestions | Android Developers からのメモ書き。

表示する検索候補をコントロールするために ContentProvider を実装する以外、検索履歴を表示する場合とほぼ変わりません。

  1. ContentProvier を実装する
    • query (と onCreate)だけ実装すれば良い
  2. AndroidManifest.xml に provider を設定する
  3. searchable.xml に authority 設定する
ContentProvider の実装
  • SearchView のテキストが変化するたびに query 関数が呼ばれる
  • query で返す Cursor には BaseColumns._ID と SearchManager.SUGGEST_COLUMN_TEXT_1 の Columns が必須
    • SUGGEST_COLUMN_TEXT_2 や SUGGEST_COLUMN_ICON_1 などが任意に追加可能

query に渡される引数は以下のとおりです。SearchView で使用する ContentProvider の場合、基本的に気にするのは selectionArgs[0] のみになります。

引数
uri content://(authority)/search_suggest_query?limit=50
projection 常に null
selection searchable.xmlandroid:searchSuggestSelection に指定した値
selectionArgs [0] に SearchView に入力されている文字列
sortOrder 常に null

(searchable.xmlandroid:searchSuggestSelection を指定していない場合、uri の方に "content://(authority)/search_suggest_query(/SearchView に入力された文字)?limit=50" という形式で検索語が入る代わりに、selectionArgs は null になる。path を分解する必要が出てくるので、android:searchSuggestSelection の使用を推奨。)

SQL を使わずに、決められた検索語から候補を絞る場合は MatrixCursor を使用して以下の様な感じに。 データの絞り込みには Guavaを利用しています。

public class MySearchSuggestionsProvider extends ContentProvider {

    String[] mSuggestionColumns;
    List<Object[]> mSuggestionItems;

    @Override
    public boolean onCreate() {
        // query で返すカラム。ここでは必須のみ。
        mSuggestionColumns = new String[] { BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1 };
        // サンプルデータの作成。後で MatrixCursor に追加するため、Object の配列。
        mSuggestionItems = new ArrayList<>();
        for(int i = 1; i <= 50; i++) {
            mSuggestionItems.add( new Object[] { i, String.valueOf(i) } );
        }
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, final String[] selectionArgs, String sortOrder) {
        // 検索文字列がない場合は何も返さず非表示に。
        if(TextUtils.isEmpty(selectionArgs[0])) {
            return null;
        }

        MatrixCursor matrixCursor = new MatrixCursor(mSuggestionColumns);
        // Guava を使用したフィルタリング
        Collection<Object[]> candidates = Collections2.filter(mSuggestionItems, new Predicate<Object[]>() {
            @Override
            public boolean apply(Object[] input) {
                return ((String)input[1]).contains(selectionArgs[0]);
            }
        });
        for(Object[] candidate : candidates) {
            matrixCursor.addRow(candidate);
        }
        return matrixCursor;
    }
    // ...他のメソッドは return null や return 0.
AndroidManifest.xml への の登録
<application
    ... >
    
    <provider
        android:authorities="(権限文字列。基本的にパッケージ名 + ContentProvider のクラス名)"
        android:name="(ContentProvider クラスのパス)"/>
searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="@string/app_name"
    android:searchSuggestAuthority="(権限名)"
    android:searchSuggestSelection=" ?"
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer">
</searchable>