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

2016年12月22日(編集2016年12月22日)
このエントリーをはてなブックマークに追加

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

難易度

中級者向け

サンプルコード

Android-RecyclerView-Demo

Search Suggestion

アプリにSearch Suggestionを実装すると、ユーザーに便利な検索エクスペリエンスを提供できます。

検索

Search Suggestionの実装方法は、少々特異です。ContentProviderの実装経験がないと、少し苦労するかもしれません。
しかし、苦労する価値のある機能なので、是非実装に挑戦してみてください。

検索窓実装

サンプルとして、アイテム名を検索する画面を作成します。

まずは、Material designのSearchViewで検索窓を実装します。
SearchViewについて全く知識がない場合は、こちらの記事を参考にしてください。

XMLでMenuを作成します。

{project_folder}/res/menu/book.xml
<?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メソッドをオーバーライドします。

{project_folder}/ui/SearchRecyclerViewActivity.java
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Set Menu
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.search, menu);
        return super.onCreateOptionsMenu(menu);
    }
        

ビルドして画面を確認します。

図 android.support.v7.widget.SearchView

画面に検索ボタンが追加されました。

XMLで指定したSearchViewをコードで実装します。

{project_folder}/ui/SearchRecyclerViewActivity.java
    @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ファイルに以下のおまじないを記述します。

{project_folder}/app/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を受けとるコードを記述します。

{project_folder}/ui/SearchRecyclerViewActivity.java
    @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を作成します。

{project_folder}/res/xml/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の動作を確認します。

図 3 android.support.v7.widget.SearchView

consoleで確認します。

D/Search: あかさたな
        

Intent.ACTION_SEARCH(android.intent.action.SEARCH)を取得して、検索文字が取得できました。

SearchRecentSuggestionsProvider

Androidでは、Search Suggestionの実装クラスにSearchRecentSuggestionsProviderが用意されています。

SearchRecentSuggestionsProviderクラスを使用すると、アプリケーション用の簡単な検索提案プロバイダを作成できます。
ここでは、ItemオブジェクトのSearch Suggestionを行うItemSearchRecentSuggestionsProviderを作成します。

{project_folder}/provider/ItemSearchRecentSuggestionsProvider.java
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に設定します。

{project_folder}/app/AndroidManifest.xml
<application>

 +<provider
      android:name=".provider.ItemSearchRecentSuggestionsProvider"
      android:authorities="com.java_lang_programming.android_recycleview_demo.provider.ItemSearchRecentSuggestionsProvider" />

  <activity>
  </activity>

</application>
        

providerタグは、applicationタグの中に記述します。
applicationタグのすぐ下に記述することが多いです。

次にproviderをsearchable.xmlに追加します。

{project_folder}/res/xml/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を実装します。

{project_folder}/ui/SearchRecyclerViewActivity.java
    @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を実装すると、ユーザーの利便性があがります。
実装は少々手間がかかりますが、その価値はあると思います。

Providing Search with SearchView (Android Development Patterns Ep 7)

関連記事

タグ検索で調べてみよう

Android7.0 UI