カスタムの検索候補を追加する

Compose をお試しください
Jetpack Compose は、Android で推奨される UI ツールキットです。Compose で検索機能を追加する方法について説明します。

Android 検索ダイアログまたは検索ウィジェットで、最近の検索クエリに基づく検索候補を表示できます。たとえば、ユーザーが「puppies」を検索した場合、そのユーザーが同じクエリを再度入力すると、そのクエリが候補として表示されます。図 1 に、最近のクエリに基づく候補が表示されている検索ダイアログの例を示します。

アプリで基本的な検索を行えるようにするには、まず、検索ダイアログまたは検索ウィジェットを実装する必要があります。詳細については、検索インターフェースを作成するをご覧ください。

基本情報

図 1. 最近のクエリに基づく候補が表示された検索ダイアログのスクリーンショット

最近のクエリに基づく候補は、保存済みの検索条件です。ユーザーが候補を選択すると、検索可能なアクティビティが候補を含む ACTION_SEARCH インテントを検索クエリとして受け取ります。この検索クエリは、検索可能なアクティビティによってすでに処理されたものです。

最近のクエリに基づく候補を表示するには、以下の操作を行う必要があります。

  • 検索可能アクティビティを実装します。
  • SearchRecentSuggestionsProvider を拡張するコンテンツ プロバイダを作成し、アプリ マニフェストで宣言します。
  • 検索候補を提供するコンテンツ プロバイダの情報を使用して、検索可能性の設定を変更します。
  • 検索が実行されるたびにコンテンツ プロバイダにクエリを保存します。

Android システムは、検索ダイアログを表示するのと同じように、検索ダイアログまたは検索ウィジェットの下に検索候補も表示します。システムが候補を取得するソースを指定します。

アクティビティが検索可能であり、検索候補を表示できることがシステムで確認されると、ユーザーがクエリを入力したときに次の手順が行われます。

  1. システムが検索クエリのテキスト(ユーザーが入力し始めたすべてのテキスト)を取得し、候補を格納するコンテンツ プロバイダに対してクエリを実行します。
  2. コンテンツ プロバイダが、検索クエリのテキストと一致するすべての候補を指す Cursor を返します。
  3. Cursor から提供された候補のリストが表示されます。

最近のクエリに基づく候補が表示された後、以下の処理が行われることがあります。

  • ユーザーが別のキーを入力するか、なんらかの方法でクエリを変更した場合、上記の手順が再実行され、候補のリストが更新されます。
  • ユーザーが検索を実行した場合、候補が無視され、通常の ACTION_SEARCH インテントを使用して検索クエリが検索可能アクティビティに渡されます。
  • ユーザーが候補を選択した場合、候補のテキストをクエリとして使用して、ACTION_SEARCH インテントが検索可能なアクティビティに渡されます。

コンテンツ プロバイダ用に拡張した SearchRecentSuggestionsProvider クラスが上記の処理を自動的に行うため、記述するコードはほとんどありません。

コンテンツ プロバイダを作成する

最近のクエリに基づく候補を表示するのに必要なコンテンツ プロバイダは、SearchRecentSuggestionsProvider の実装です。このクラスがすべての処理を行います。デベロッパーが行う必要がある操作は、1 行のコードを実行するクラス コンストラクタを記述することだけです。

以下に、最近のクエリに基づく候補を表示するのに必要なコンテンツ プロバイダの完全な実装例を示します。

Kotlin

class MySuggestionProvider : SearchRecentSuggestionsProvider() {
    init {
        setupSuggestions(AUTHORITY, MODE)
    }

    companion object {
        const val AUTHORITY = "com.example.MySuggestionProvider"
        const val MODE: Int = SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES
    }
}

Java

public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
    public final static String AUTHORITY = "com.example.MySuggestionProvider";
    public final static int MODE = DATABASE_MODE_QUERIES;

    public MySuggestionProvider() {
        setupSuggestions(AUTHORITY, MODE);
    }
}

setupSuggestions() の呼び出しでは、検索権限の名前とデータベースのモードを渡しています。検索権限には任意の一意の文字列を指定できますが、コンテンツ プロバイダの完全修飾名(パッケージ名に続いてプロバイダのクラス名)を使用することをおすすめします。例: "com.example.MySuggestionProvider"

データベースのモードには DATABASE_MODE_QUERIES を含める必要があります。また、必要に応じて DATABASE_MODE_2LINES を含めることもできます。これにより、候補テーブルに列が追加され、各候補で 2 行目のテキストを指定できるようになります。各候補で 2 つの行を指定する場合は、次の例をご覧ください。

Kotlin

const val MODE: Int = DATABASE_MODE_QUERIES or DATABASE_MODE_2LINES

Java

public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;

SearchRecentSuggestionsProvider クラスと検索可能性の設定で使用するのと同じ権限文字列を使用して、アプリ マニフェストでコンテンツ プロバイダを宣言します。次に例を示します。

<application>
    <provider android:name=".MySuggestionProvider"
              android:authorities="com.example.MySuggestionProvider" />
    ...
</application>

検索可能構成を変更する

候補プロバイダを使用するようにシステムを設定するには、android:searchSuggestAuthority 属性と android:searchSuggestSelection 属性を検索可能性の設定ファイルの <searchable> 要素に追加します。次に例を示します。

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.MySuggestionProvider"
    android:searchSuggestSelection=" ?" >
</searchable>

android:searchSuggestAuthority の値には、コンテンツ プロバイダで使用されている権限と完全に一致する、コンテンツ プロバイダの完全修飾名(前の例の "com.example.MySuggestionProvider" など)を指定する必要があります。

android:searchSuggestSelection の値には、スペースとそれに続く 1 つの疑問符(" ?")を指定する必要があります。これは SQLite の選択引数のプレースホルダであり、ユーザーが入力したクエリテキストで自動的に置き換えられます。

クエリを保存する

最近のクエリのコレクションを作成するには、検索可能なアクティビティで受け取った各クエリを SearchRecentSuggestionsProvider に追加します。そのためには、SearchRecentSuggestions のインスタンスを作成し、検索可能なアクティビティがクエリを受け取るたびに saveRecentQuery() を呼び出します。以下に、アクティビティの onCreate() メソッドでクエリを保存する方法の例を示します。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)

    if (Intent.ACTION_SEARCH == intent.action) {
        intent.getStringExtra(SearchManager.QUERY)?.also { query ->
            SearchRecentSuggestions(this, MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE)
                    .saveRecentQuery(query, null)
        }
    }
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent  = getIntent();

    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
        String query = intent.getStringExtra(SearchManager.QUERY);
        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
                MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);
        suggestions.saveRecentQuery(query, null);
    }
}

SearchRecentSuggestionsProvider コンストラクタでは、コンテンツ プロバイダが宣言したのと同じ権限とデータベース モードが必要です。

saveRecentQuery() メソッドは、1 つ目のパラメータとして検索クエリ文字列を受け取ります。また、候補の 2 行目として含める 2 つ目の文字列(または null)を必要に応じて受け取ります。2 つ目のパラメータは、DATABASE_MODE_2LINES を指定して検索候補の 2 行モードを有効にした場合にのみ使用されます。2 行モードを有効にした場合、システムが一致する候補を探す際に、クエリテキストが 2 行目と照合されます。

候補データを消去する

ユーザーのプライバシーを保護するために、最近のクエリに基づく候補を消去する方法をユーザーに提供する必要があります。クエリ履歴を消去するには、clearHistory() を呼び出します。次に例を示します。

Kotlin

SearchRecentSuggestions(this, HelloSuggestionsProvider.AUTHORITY, HelloSuggestionsProvider.MODE)
        .clearHistory()

Java

SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
        HelloSuggestionProvider.AUTHORITY, HelloSuggestionProvider.MODE);
suggestions.clearHistory();

この操作は、[検索履歴を消去] メニュー項目、ユーザー設定項目、ボタンのいずれか(デベロッパーが選択)から実行します。ユーザーが本当に検索履歴の削除を求めていることを確認するための確認ダイアログを表示します。