検索インターフェースを設定する

Android 3.0 以降、アプリで検索を提供するには、SearchView ウィジェットをアプリバーの項目として使用することをおすすめします。SearchView についてもアプリバーの他の項目と同じように表示方法を定義して、常に表示したり、スペースがある場合にのみ表示したりできます。または、折りたたみ可能なアクションとして定義すると、最初は SearchView がアイコンとして表示され、ユーザーがアイコンをクリックするとアプリバー全体が検索フィールドになります。

注: このコースの後半では、SearchView をサポートしていない Android 2.1(API レベル 7)を搭載したデバイスまで対象にして、下位互換性をアプリで実現する方法について紹介します。

SearchView をアプリバーに追加する

SearchView ウィジェットをアプリバーに追加するには、res/menu/options_menu.xml という名前のファイルをプロジェクト内に作成し、下記のコードをそのファイルに含めます。このコードは、アプリバーで使用するアイコンや項目のタイトルなど、検索項目の作成方法を定義するものです。collapseActionView 属性を使用すると、SearchView を展開してアプリバー全体に表示したり、使用しないときには折りたたんでアプリバーの通常の項目に戻したりすることができます。ハンドセット デバイス上ではアプリバーのスペースが限られるので、collapsibleActionView 属性を使ってユーザー エクスペリエンスの向上を図ることをおすすめします。

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/search"
              android:title="@string/search_title"
              android:icon="@drawable/ic_search"
              android:showAsAction="collapseActionView|ifRoom"
              android:actionViewClass="android.widget.SearchView" />
    </menu>
    

注: メニュー項目用の XML ファイルがすでにあれば、代わりにそのファイルに <item> 要素を追加することができます。

SearchView をアプリバーに表示するには、アプリのアクティビティの onCreateOptionsMenu() メソッド内に XML メニュー リソース(res/menu/options_menu.xml)をインフレートします。

Kotlin

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.options_menu, menu)

        return true
    }
    

Java

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.options_menu, menu);

        return true;
    }
    

ここでアプリを実行すると、SearchView はアプリのアプリバーに表示されますが、まだ機能しません。これから、SearchView がどのように動作するかを定義する必要があります。

検索可能性の設定を作成する

検索可能性の設定は、SearchView の動作を res/xml/searchable.xml ファイル内に定義するものです。検索可能性の設定に最小限必要なのは android:label 属性です。この属性の値は、Android マニフェスト内の <application> 要素または <activity> 要素の android:label 属性と同じ値です。ただし他にも、android:hint 属性を指定して、検索ボックスに何を入力するかのヒントを表示することをおすすめします。

    <?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/search_hint" />
    

アプリのマニフェスト ファイルでは、<meta-data> 要素を宣言し、res/xml/searchable.xml ファイルを指すように設定して、アプリがどこを探せばよいかを伝えます。この要素は、SearchView を表示する <activity> 内で宣言します。

    <activity ... >
        ...
        <meta-data android:name="android.app.searchable"
                android:resource="@xml/searchable" />

    </activity>
    

これまでに作成した onCreateOptionsMenu() メソッド内で setSearchableInfo(SearchableInfo) を呼び出して、検索可能性の設定を SearchView と関連付けます。

Kotlin

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.options_menu, menu)

        // Associate searchable configuration with the SearchView
        val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
        (menu.findItem(R.id.search).actionView as SearchView).apply {
            setSearchableInfo(searchManager.getSearchableInfo(componentName))
        }

        return true
    }
    

Java

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.options_menu, menu);

        // Associate searchable configuration with the SearchView
        SearchManager searchManager =
               (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView =
                (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setSearchableInfo(
                searchManager.getSearchableInfo(getComponentName()));

        return true;
    }
    

getSearchableInfo() を呼び出すと、検索可能性の設定の XML ファイルから作成される SearchableInfo オブジェクトを取得できます。検索可能性の設定が SearchView に正常に関連付けられると、SearchView はユーザーからのクエリの送信時に、ACTION_SEARCH インテントを持つアクティビティを開始します。ここで、このインテントをフィルタし、その検索クエリを処理するアクティビティが必要です。

検索可能なアクティビティを作成する

ユーザーが検索クエリを送信すると、SearchViewACTION_SEARCH を持つアクティビティを開始しようとします。検索可能なアクティビティは ACTION_SEARCH インテントをフィルタし、なんらかのデータセットでそのクエリを検索します。検索可能なアクティビティを作成するには、必要なアクティビティを宣言して、ACTION_SEARCH インテントをフィルタします。

    <activity android:name=".SearchResultsActivity" ... >
        ...
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        ...
    </activity>
    

検索可能なアクティビティでは、onCreate() メソッド内で ACTION_SEARCH インテントを確認し、処理します。

注: 検索可能なアクティビティがシングルトップ モード(android:launchMode="singleTop")で開始される場合、ACTION_SEARCH インテントも onNewIntent() メソッド内で処理します。シングルトップ モードでは、アクティビティのインスタンスは 1 つしか作成されず、そのアクティビティを開始する呼び出しがその後にあっても、新しいアクティビティがスタック上に作成されることはありません。毎回新しいアクティビティのインスタンスが作成されることなく、ユーザーが同じアクティビティから検索できるので、この開始モードは便利です。

Kotlin

    class SearchResultsActivity : Activity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            handleIntent(intent)
        }

        override fun onNewIntent(intent: Intent) {
            ...
            handleIntent(intent)
        }

        private fun handleIntent(intent: Intent) {

            if (Intent.ACTION_SEARCH == intent.action) {
                val query = intent.getStringExtra(SearchManager.QUERY)
                //use the query to search your data somehow
            }
        }
        ...
    }
    

Java

    public class SearchResultsActivity extends Activity {

        @Override
        public void onCreate(Bundle savedInstanceState) {
            ...
            handleIntent(getIntent());
        }

        @Override
        protected void onNewIntent(Intent intent) {
            ...
            handleIntent(intent);
        }

        private void handleIntent(Intent intent) {

            if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
                String query = intent.getStringExtra(SearchManager.QUERY);
                //use the query to search your data somehow
            }
        }
        ...
    }
    

ここでアプリを実行すると、SearchView はユーザーのクエリを受け付けて、ACTION_SEARCH インテントを持つ検索可能なアクティビティを開始できます。この後は、クエリに返すデータを保存する方法と検索する方法を検討してください。