Androidアプリ開発 Material design SearchView Search Suggestionを実装する

Androidアプリ開発では、SearchViewを使ってSearch Suggestionを実装することができます。
この記事は、SearchViewでSearch Suggestionの実装方法を記載した記事です。
環境はAndroid 7.1 (API level 25) です。
環境
- OS X Yosemite
- Oracle jdk version 1.8.0_72
- Android Studio 2.2.3
- android sdk 25
難易度
中級者向け
サンプルコード
Search Suggestion
アプリにSearch Suggestionを実装すると、ユーザーに便利な検索エクスペリエンスを提供できます。

Search Suggestionの実装方法は、少々特異です。ContentProviderの実装経験がないと、少し苦労するかもしれません。
しかし、苦労する価値のある機能なので、是非実装に挑戦してみてください。
検索窓実装
サンプルとして、アイテム名を検索する画面を作成します。
まずは、Material designのSearchViewで検索窓を実装します。
SearchViewについて全く知識がない場合は、こちらの記事を参考にしてください。
XMLでMenuを作成します。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/search" android:icon="@android:drawable/ic_menu_search" android:title="Search" app:actionViewClass="android.support.v7.widget.SearchView" app:showAsAction="ifRoom|collapseActionView" /> </menu>
作成したメニューをActivityで使います。
アクティビティのオプションメニューを指定するには、 onCreateOptionsMenuメソッドをオーバーライドします。
@Override public boolean onCreateOptionsMenu(Menu menu) { // Set Menu MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.search, menu); return super.onCreateOptionsMenu(menu); }
ビルドして画面を確認します。

画面に検索ボタンが追加されました。
XMLで指定したSearchViewをコードで実装します。
@Override public boolean onCreateOptionsMenu(Menu menu) { // Set Menu MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.search, menu); SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { return false; } }); return super.onCreateOptionsMenu(menu); }
上記のコードが理解できない場合は、この記事で理解してください。
検索可能にする
次に、検索ができるように設定をします。
検索機能を利用するActivityを決めたら、AndroidManifest.xmlファイルに以下のおまじないを記述します。
<activity android:name=".ui.SearchRecyclerViewActivity" android:label="@string/title_activity_search_recycler_view" android:theme="@style/AppTheme.NoActionBar"> +<intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> +<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity>
■ 実装の解説
1. intent-filter
activityタグの中に、intent-filterを記述します。
intent-filterは、アプリが受け取ることのできるインテントを設定できます。つまり、上記のSearchRecyclerViewActivityは、android.intent.action.SEARCHを受けとることができるようになります。
Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { // something }
2. meta-data
activityタグの中に、meta-dataタグを記述します。
meta-dataは、親コンポーネント(ここではSearchRecyclerViewActivity)に供給できる追加の任意のデータ項目の名前と値のペアを設定できます。
検索機能の実装では、値を設定する属性android:valueの代わりに、android:resource属性を使います。resource属性を使用すると、resourceファイルを参照することができます。
ここでは、外部ファイルのxml/searchable.xmlを読み込んでいます。
上記の設定を終えたら、Activityにandroid.intent.action.SEARCHを受けとるコードを記述します。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_recycler_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FragmentManager fragmentManager = getSupportFragmentManager();
recyclerViewFragment = (SearchRecyclerViewFragment) fragmentManager.findFragmentById(R.id.search_recycler_view_frag);
+Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.d("Search", query);
}
}
■ 実装の解説
1. intent取得
onCreateメソッドでIntentを受け取ります。
受け取る値は、intent-filterで設定したandroid.intent.action.SEARCHです。
if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); Log.d("Search", query); }
intentがIntent.ACTION_SEARCHの場合は、Activityで検索処理が実行されたことになります。
query文字列から検索文字が取得できます。
searchable.xml設定
AndroidManifest.xmlのActivityにmeta-dataの設定をしたら、android:resource属性に記述した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/searchable_hint"> </searchable>
■ 実装の解説
1. searchable.xml
このファイルはsearchable configurationと呼ばれていて、res/xml/フォルダの下に置くのが一般的です。ファイル名は他の名前でも問題ありません。
Androidシステムは、このsearchable.xmlファイルを使用してSearchableInfoオブジェクトをインスタンス化します。
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
ビルドして画面とIntentの動作を確認します。

consoleで確認します。
D/Search: あかさたな
Intent.ACTION_SEARCH(android.intent.action.SEARCH)を取得して、検索文字が取得できました。
SearchRecentSuggestionsProvider
Androidでは、Search Suggestionの実装クラスにSearchRecentSuggestionsProviderが用意されています。
SearchRecentSuggestionsProviderクラスを使用すると、アプリケーション用の簡単な検索提案プロバイダを作成できます。
ここでは、ItemオブジェクトのSearch Suggestionを行うItemSearchRecentSuggestionsProviderを作成します。
public class ItemSearchRecentSuggestionsProvider extends SearchRecentSuggestionsProvider { // package name + class name public static final String AUTHORITY = "com.java_lang_programming.android_recycleview_demo.provider.ItemSearchRecentSuggestionsProvider"; public static final int MODE = DATABASE_MODE_QUERIES; public ItemSearchRecentSuggestionsProvider() { super(); setupSuggestions(AUTHORITY, MODE); } }
■ 実装の解説
1. AUTHORITY
AUTHORITYは、コンテンツプロバイダによって提供されるデータを識別するURIです。
AUTHORITYの名称は、世界で一意にする必要があります。
なので、パッケージ名 + クラス名にするのが一般的です。
public static final String AUTHORITY = "com.java_lang_programming.android_recycleview_demo.provider.ItemSearchRecentSuggestionsProvider";
2. DATABASE_MODE_QUERIES
モードフラグで、データベースの特定の機能を決定することができます。
DATABASE_MODE_QUERIESは、最近のクエリを記録するデータベースを構成します。
public static final int MODE = DATABASE_MODE_QUERIES;
3. setupSuggestions
SearchRecentSuggestionsProviderを有効にするには、コンストラクタでsetupSuggestionsメソッドを呼びだす必要があります。
public ItemSearchRecentSuggestionsProvider() { super(); setupSuggestions(AUTHORITY, MODE); }
上記で作成したItemSearchRecentSuggestionsProviderをAndroidManifest.xmlに設定します。
<application>
+<provider
android:name=".provider.ItemSearchRecentSuggestionsProvider"
android:authorities="com.java_lang_programming.android_recycleview_demo.provider.ItemSearchRecentSuggestionsProvider" />
<activity>
</activity>
</application>
applicationタグのすぐ下に記述することが多いです。
次にproviderを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/searchable_hint"
+android:searchSuggestAuthority="com.java_lang_programming.android_recycleview_demo.provider.ItemSearchRecentSuggestionsProvider"
android:searchSuggestSelection=" ?"
</searchable>
Activityに上記で追加したItemSearchRecentSuggestionsProviderを実装します。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_recycler_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FragmentManager fragmentManager = getSupportFragmentManager();
recyclerViewFragment = (SearchRecyclerViewFragment) fragmentManager.findFragmentById(R.id.search_recycler_view_frag);
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.d("Search", query);
getSupportActionBar().setTitle(query);
+SearchRecentSuggestions searchRecentSuggestions = new SearchRecentSuggestions(getApplicationContext(),
ItemSearchRecentSuggestionsProvider.AUTHORITY,
ItemSearchRecentSuggestionsProvider.MODE);
searchRecentSuggestions.saveRecentQuery(query, null);
}
}
ビルド
ビルドしてアプリを立ち上げます。
検索して、再度検索窓を開きます。

Search Suggestionが表示されました。
まとめ
Search Suggestionを実装すると、ユーザーの利便性があがります。
実装は少々手間がかかりますが、その価値はあると思います。