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

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

Android 検索ダイアログまたは検索ウィジェットで、最近の検索クエリに基づく検索候補を表示できます。たとえば、ユーザーが「子犬」を検索した場合、同じクエリを再度入力すると、そのクエリが候補として表示されます。図 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" のように、パッケージ名に続いてプロバイダのクラス名を指定します)を使用することをおすすめします。例: "com.example.MySuggestionProvider"

データベースのモードには DATABASE_MODE_QUERIES を含める必要があります。また、必要に応じて DATABASE_MODE_2LINES を含めることもできます。これにより、候補テーブルに列がもう 1 つ追加され、各候補で 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>

検索可能性の設定を変更する

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

<?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() メソッドは、検索クエリ文字列を最初のパラメータとして受け取ります。また、必要に応じて、候補の 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();

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