連絡先のリストを取得する

このレッスンでは、検索文字列の全部または一部にデータが一致する連絡先のリストを取得する方法を示します。使用する手法は以下のとおりです。

連絡先の名前を照合する
検索文字列を連絡先の名前データの全部または一部と照合して、連絡先のリストを取得します。連絡先プロバイダでは重複する名前での登録が許可されているため、一致する名前が複数返されることもあり得ます。
特定の種類のデータ(電話番号など)を照合する
検索文字列を特定の種類の詳細データ(メールアドレスなど)と照合して、連絡先のリストを取得します。たとえば、メールアドレスが検索文字列と一致するすべての連絡先を一覧表示できます。
すべての種類のデータを照合する
検索文字列を名前、電話番号、住所、メールアドレスなどのすべての種類の詳細データと照合して、連絡先のリストを取得します。たとえば、検索文字列に任意の型のデータを受け入れ、データが文字列に一致する連絡先を一覧表示できます。

注: このレッスンでは、すべての例で CursorLoader を使用して連絡先プロバイダからデータを取得します。CursorLoader は、UI スレッドとは別のスレッドでクエリを実行します。そのため、クエリの負荷で UI の応答時間が遅くなって、ユーザー エクスペリエンスが低下する、ということはありません。詳しくは、Android トレーニング クラスのバックグラウンドでのデータの読み込みをご覧ください。

プロバイダの読み取り権限をリクエストする

連絡先プロバイダでなんらかの種類の検索を行うには、アプリに READ_CONTACTS 権限が必要です。 この権限をリクエストするには、<uses-permission> 要素を <manifest> の子要素としてマニフェスト ファイルに追加します。

        <uses-permission android:name="android.permission.READ_CONTACTS" />
    

連絡先を名前で照合して結果を一覧表示する

この手法では、検索文字列を、連絡先プロバイダの ContactsContract.Contacts テーブル内にある連絡先の名前と照合します。通常、結果を ListView に表示して、ユーザーが一致した連絡先から選択できるようにします。

ListView とアイテム レイアウトを定義する

検索結果を ListView に表示するには、ListView を含む UI 全体を定義するメイン レイアウト ファイルと、1 行の ListView を定義するアイテム レイアウト ファイルが必要です。たとえば、次の XML を使用してメイン レイアウト ファイル res/layout/contacts_list_view.xml を作成できます。

    <?xml version="1.0" encoding="utf-8"?>
    <ListView xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@android:id/list"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>
    

この XML は、Android 内蔵の ListView ウィジェットである android:id/list を使用します。

次の XML を使用して、アイテム レイアウト ファイル contacts_list_item.xml を定義します。

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@android:id/text1"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:clickable="true"/>
    

この XML は、Android 内蔵の TextView ウィジェットである android:text1 を使用します。

注: このレッスンでは、ユーザーから検索文字列を取得するための UI については説明しません。ユーザーから間接的に文字列を取得した方がよいからです。たとえば、受信テキスト メッセージ内の文字列と名前が一致する連絡先を検索するオプションをユーザーに提供できます。

作成した 2 つのレイアウト ファイルは、ListView を表示するユーザー インターフェースを定義します。次の手順では、この UI を使用して連絡先のリストを表示するコードを記述します。

連絡先のリストを表示するフラグメントを定義する

連絡先のリストを表示するには、Activity がロードする Fragment を定義することから始めます。Fragment を使用する手法は柔軟性に優れています。1 つの Fragment でリストを表示し、もう 1 つの Fragment でユーザーがリストから選択した連絡先の詳細を表示できます。このアプローチを使用すると、このレッスンで紹介した手法のいずれかと、連絡先の詳細を取得するで紹介する手法を組み合わせることができます。

Activity から 1 つまたは複数の Fragment オブジェクトを使用する方法については、トレーニング クラスのフラグメントを使って動的 UI を作成するをご覧ください。

連絡先プロバイダに対するクエリを作成しやすくするために、Android フレームワークは ContactsContract と呼ばれるコントラクト クラスを提供しています。このクラスでは、プロバイダにアクセスするための便利な定数とメソッドを定義します。このクラスを使用する場合、コンテンツ URI、テーブル名、列用の独自の定数を定義する必要はありません。このクラスを使用するには、次のステートメントを含めます。

Kotlin

    import android.provider.ContactsContract
    

Java

    import android.provider.ContactsContract;
    

コードは CursorLoader を使用してプロバイダからデータを取得するため、ローダ インターフェース LoaderManager.LoaderCallbacks を実装するように指定する必要があります。また、ユーザーが検索結果のリストから選択した連絡先を検出するために、アダプター インターフェース AdapterView.OnItemClickListener を実装します。次に例を示します。

Kotlin

    ...
    import android.support.v4.app.Fragment
    import android.support.v4.app.LoaderManager
    import android.widget.AdapterView
    ...
    class ContactsFragment :
            Fragment(),
            LoaderManager.LoaderCallbacks<Cursor>,
            AdapterView.OnItemClickListener {
    

Java

    ...
    import android.support.v4.app.Fragment;
    import android.support.v4.app.LoaderManager.LoaderCallbacks;
    import android.widget.AdapterView;
    ...
    public class ContactsFragment extends Fragment implements
            LoaderManager.LoaderCallbacks<Cursor>,
            AdapterView.OnItemClickListener {
    

グローバル変数を定義する

コードの他の部分で使用されるグローバル変数を定義します。

Kotlin

    ...
    /*
     * Defines an array that contains column names to move from
     * the Cursor to the ListView.
     */
    @SuppressLint("InlinedApi")
    private val FROM_COLUMNS: Array<String> = arrayOf(
            if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) {
                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
            } else {
                ContactsContract.Contacts.DISPLAY_NAME
            }
    )
    /*
     * Defines an array that contains resource ids for the layout views
     * that get the Cursor column contents. The id is pre-defined in
     * the Android framework, so it is prefaced with "android.R.id"
     */
    private val TO_IDS: IntArray = intArrayOf(android.R.id.text1)
    ...
    class ContactsFragment :
            Fragment(),
            LoaderManager.LoaderCallbacks<Cursor>,
            AdapterView.OnItemClickListener {
        ...
        // Define global mutable variables
        // Define a ListView object
        lateinit var contactsList: ListView
        // Define variables for the contact the user selects
        // The contact's _ID value
        var contactId: Long = 0
        // The contact's LOOKUP_KEY
        var contactKey: String? = null
        // A content URI for the selected contact
        var contactUri: Uri? = null
        // An adapter that binds the result Cursor to the ListView
        private val cursorAdapter: SimpleCursorAdapter? = null

    

Java

        ...
        /*
         * Defines an array that contains column names to move from
         * the Cursor to the ListView.
         */
        @SuppressLint("InlinedApi")
        private final static String[] FROM_COLUMNS = {
                Build.VERSION.SDK_INT
                        >= Build.VERSION_CODES.HONEYCOMB ?
                        ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                        ContactsContract.Contacts.DISPLAY_NAME
        };
        /*
         * Defines an array that contains resource ids for the layout views
         * that get the Cursor column contents. The id is pre-defined in
         * the Android framework, so it is prefaced with "android.R.id"
         */
        private final static int[] TO_IDS = {
               android.R.id.text1
        };
        // Define global mutable variables
        // Define a ListView object
        ListView contactsList;
        // Define variables for the contact the user selects
        // The contact's _ID value
        long contactId;
        // The contact's LOOKUP_KEY
        String contactKey;
        // A content URI for the selected contact
        Uri contactUri;
        // An adapter that binds the result Cursor to the ListView
        private SimpleCursorAdapter cursorAdapter;
        ...
    

注: Contacts.DISPLAY_NAME_PRIMARY を使用するには、Android 3.0(API バージョン 11)以降が必要です。アプリの minSdkVersion を 10 以下に設定すると、Android Studio で Android Lint 警告が生成されます。この警告をオフにするには、アノテーション @SuppressLint("InlinedApi")FROM_COLUMNS の定義の前に追加します。

フラグメントを初期化する

Fragment を初期化します。Android システムに必要な空のパブリック コンストラクタを追加し、コールバック メソッド onCreateView()Fragment オブジェクトの UI をインフレートします。次に例を示します。

Kotlin

        // A UI Fragment must inflate its View
        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View? {
            // Inflate the fragment layout
            return inflater.inflate(R.layout.contact_list_fragment, container, false)
        }
    

Java

        // Empty public constructor, required by the system
        public ContactsFragment() {}

        // A UI Fragment must inflate its View
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            // Inflate the fragment layout
            return inflater.inflate(R.layout.contact_list_fragment,
                container, false);
        }
    

ListView の CursorAdapter を設定する

検索結果を ListView にバインドする SimpleCursorAdapter を設定します。連絡先を表示する ListView オブジェクトを取得するには、Fragment の親アクティビティを使用して Activity.findViewById() を呼び出す必要があります。setAdapter() を呼び出すときには、親アクティビティの Context を使用します。次に例を示します。

Kotlin

        override fun onActivityCreated(savedInstanceState: Bundle?) {
            super.onActivityCreated(savedInstanceState)
            ...
            // Gets the ListView from the View list of the parent activity
            activity?.also {
                contactsList = it.findViewById<ListView>(R.id.contact_list_view)
                // Gets a CursorAdapter
                cursorAdapter = SimpleCursorAdapter(
                        it,
                        R.layout.contact_list_item,
                        null,
                        FROM_COLUMNS, TO_IDS,
                        0
                )
                // Sets the adapter for the ListView
                contactsList.adapter = cursorAdapter
            }
        }
    

Java

        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            ...
            // Gets the ListView from the View list of the parent activity
            contactsList =
                (ListView) getActivity().findViewById(R.layout.contact_list_view);
            // Gets a CursorAdapter
            cursorAdapter = new SimpleCursorAdapter(
                    getActivity(),
                    R.layout.contact_list_item,
                    null,
                    FROM_COLUMNS, TO_IDS,
                    0);
            // Sets the adapter for the ListView
            contactsList.setAdapter(cursorAdapter);
        }
    

選択した連絡先リスナーを設定する

検索結果を表示するとき、通常は、ユーザーが検索結果から 1 つの連絡先を選択して、さらに処理を行えるようにしたいでしょう。たとえば、ユーザーがクリックした連絡先の住所を地図上に表示することができます。この機能を提供するには、まず現在の Fragment をクリック リスナーとして定義します。そのためには、このクラスが AdapterView.OnItemClickListener を実装するよう指定ます。詳しくは、連絡先のリストを表示するフラグメントを定義するをご覧ください。

リスナーの設定を続行するには、onActivityCreated()setOnItemClickListener() メソッドを呼び出してリスナーを ListView にバインドします。次に例を示します。

Kotlin

        fun onActivityCreated(savedInstanceState:Bundle) {
            ...
            // Set the item click listener to be the current fragment.
            contactsList.onItemClickListener = this
            ...
        }
    

Java

        public void onActivityCreated(Bundle savedInstanceState) {
            ...
            // Set the item click listener to be the current fragment.
            contactsList.setOnItemClickListener(this);
            ...
        }
    

現在の FragmentListViewOnItemClickListener であることを指定したため、クリック イベントの処理に必要なメソッド onItemClick() を実装する必要があります。これについては、次のセクションで説明します。

プロジェクションを定義する

クエリから返す列を含む定数を定義します。ListView の各アイテムには、連絡先の表示名が表示されます。これには、連絡先の名前のメインフォームが含まれます。この列の名前は、Android 3.0(API バージョン 11)以降では Contacts.DISPLAY_NAME_PRIMARY、それより前のバージョンでは Contacts.DISPLAY_NAME です。

列の Contacts._IDSimpleCursorAdapter バインディング プロセスによって使用されます。Contacts._IDLOOKUP_KEY を同時に使用して、ユーザーが選択した連絡先のコンテンツ URI を作成します。

Kotlin

    ...
    @SuppressLint("InlinedApi")
    private val PROJECTION: Array<out String> = arrayOf(
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.LOOKUP_KEY,
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
            else
                ContactsContract.Contacts.DISPLAY_NAME
    )
    

Java

    ...
    @SuppressLint("InlinedApi")
    private static final String[] PROJECTION =
            {
                Contacts._ID,
                Contacts.LOOKUP_KEY,
                Build.VERSION.SDK_INT
                        >= Build.VERSION_CODES.HONEYCOMB ?
                        ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                        ContactsContract.Contacts.DISPLAY_NAME

            };
    

Cursor 列のインデックスの定数を定義する

Cursor 内の各列からデータを取得するには、Cursor 内の列のインデックスが必要です。インデックスはプロジェクションの列名の順序と同じであるため、Cursor 列のインデックスに定数を定義できます。次に例を示します。

Kotlin

    // The column index for the _ID column
    private const val CONTACT_ID_INDEX: Int = 0
    // The column index for the CONTACT_KEY column
    private const val CONTACT_KEY_INDEX: Int = 1
    

Java

    // The column index for the _ID column
    private static final int CONTACT_ID_INDEX = 0;
    // The column index for the CONTACT_KEY column
    private static final int CONTACT_KEY_INDEX = 1;
    

選択基準を指定する

必要なデータを指定するには、テキスト表現と変数の組み合わせを作成して、検索するデータ列と検索する値をプロバイダに伝えます。

テキスト表現には、検索列をリストする定数を定義します。この表現には値を含めることもできますが、値をプレースホルダ「?」で表すことをおすすめします。プレースホルダは、取得時に配列の値に置き換えられます。「?」をプレースホルダとして使用すると、検索仕様が SQL コンパイルではなくバインディングによって生成されます。これにより、悪意のある SQL インジェクションの可能性が排除されます。次に例を示します。

Kotlin

    // Defines the text expression
    @SuppressLint("InlinedApi")
    private val SELECTION: String =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
                "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} LIKE ?"
            else
                "${ContactsContract.Contacts.DISPLAY_NAME} LIKE ?"
    ...
        // Defines a variable for the search string
        private val searchString: String = ...
        // Defines the array to hold values that replace the ?
        private val selectionArgs = arrayOf<String>(searchString)
    

Java

        // Defines the text expression
        @SuppressLint("InlinedApi")
        private static final String SELECTION =
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
                Contacts.DISPLAY_NAME + " LIKE ?";
        // Defines a variable for the search string
        private String searchString;
        // Defines the array to hold values that replace the ?
        private String[] selectionArgs = { searchString };
    

onItemClick() メソッドを定義する

前のセクションでは、ListView のアイテム クリック リスナーを設定しました。次に、メソッド AdapterView.OnItemClickListener.onItemClick() を定義してリスナーのアクションを実装します。

Kotlin

        override fun onItemClick(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
            // Get the Cursor
            val cursor: Cursor? = (parent.adapter as? CursorAdapter)?.cursor?.apply {
                // Move to the selected contact
                moveToPosition(position)
                // Get the _ID value
                contactId = getLong(CONTACT_ID_INDEX)
                // Get the selected LOOKUP KEY
                contactKey = getString(CONTACT_KEY_INDEX)
                // Create the contact's content Uri
                contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey)
                /*
                 * You can use contactUri as the content URI for retrieving
                 * the details for a contact.
                 */
            }
        }
    

Java

        @Override
        public void onItemClick(
            AdapterView<?> parent, View item, int position, long rowID) {
            // Get the Cursor
            Cursor cursor = parent.getAdapter().getCursor();
            // Move to the selected contact
            cursor.moveToPosition(position);
            // Get the _ID value
            contactId = cursor.getLong(CONTACT_ID_INDEX);
            // Get the selected LOOKUP KEY
            contactKey = cursor.getString(CONTACT_KEY_INDEX);
            // Create the contact's content Uri
            contactUri = Contacts.getLookupUri(contactId, mContactKey);
            /*
             * You can use contactUri as the content URI for retrieving
             * the details for a contact.
             */
        }
    

ローダを初期化する

CursorLoader を使用してデータを取得するため、非同期的な取得を制御するバックグラウンド スレッドとその他の変数を初期化する必要があります。次の例に示すように、onCreate() で初期化を行います。

Kotlin

    class ContactsFragment :
            Fragment(),
            LoaderManager.LoaderCallbacks<Cursor> {
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            // Always call the super method first
            super.onCreate(savedInstanceState)
            ...
            // Initializes the loader
            loaderManager.initLoader(0, null, this)
    

Java

    public class ContactsFragment extends Fragment implements
            LoaderManager.LoaderCallbacks<Cursor> {
        ...
        // Called just before the Fragment displays its UI
        @Override
        public void onCreate(Bundle savedInstanceState) {
            // Always call the super method first
            super.onCreate(savedInstanceState);
            ...
            // Initializes the loader
            getLoaderManager().initLoader(0, null, this);
    

onCreateLoader() を実装する

initLoader() を呼び出した直後にローダ フレームワークが呼び出す onCreateLoader() メソッドを実装します。

onCreateLoader() で検索文字列のパターンを設定します。文字列をパターンにするには、ゼロ個以上の一連の文字を表す「%(パーセント)」記号と、単一の文字を表す「_(アンダースコア)」記号のいずれかまたは両方を挿入します。たとえば、パターン「%Jefferson%」は「Thomas Jefferson」と「Jefferson Davis」の両方に一致します。

メソッドから新しい CursorLoader を返します。コンテンツ URI には、Contacts.CONTENT_URI を使用します。次の例に示すように、この URI はテーブル全体を参照します。

Kotlin

        ...
        override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
            /*
             * Makes search string into pattern and
             * stores it in the selection array
             */
            selectionArgs[0] = "%$mSearchString%"
            // Starts the query
            return activity?.let {
                return CursorLoader(
                        it,
                        ContactsContract.Contacts.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        null
                )
            } ?: throw IllegalStateException()
        }
    

Java

        ...
        @Override
        public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
            /*
             * Makes search string into pattern and
             * stores it in the selection array
             */
            selectionArgs[0] = "%" + searchString + "%";
            // Starts the query
            return new CursorLoader(
                    getActivity(),
                    ContactsContract.Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            );
        }
    

onLoadFinished() と onLoaderReset() を実装する

onLoadFinished() メソッドを実装します。連絡先プロバイダがクエリの結果を返すと、ローダ フレームワークは onLoadFinished() を呼び出します。このメソッドでは、結果の CursorSimpleCursorAdapter に格納されます。これにより、検索結果で ListView が自動的に更新されます。

Kotlin

        override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
            // Put the result Cursor in the adapter for the ListView
            cursorAdapter?.swapCursor(cursor)
        }
    

Java

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
            // Put the result Cursor in the adapter for the ListView
            cursorAdapter.swapCursor(cursor);
        }
    

onLoaderReset() メソッドは、結果の Cursor に古いデータが含まれていることをローダ フレームワークが検出したときに呼び出されます。既存の Cursor への SimpleCursorAdapter 参照は削除してください。削除しないと、ローダ フレームワークは Cursor をリサイクルせず、メモリリークが発生します。次に例を示します。

Kotlin

        override fun onLoaderReset(loader: Loader<Cursor>) {
            // Delete the reference to the existing Cursor
            cursorAdapter?.swapCursor(null)
        }
    

Java

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            // Delete the reference to the existing Cursor
            cursorAdapter.swapCursor(null);

        }
    

これで、検索文字列を連絡先の名前と照合し、結果を ListView に返すアプリの重要な部分ができました。ユーザーは連絡先の名前をクリックして選択できます。 これによりリスナーがトリガーされ、連絡先のデータをさらに操作できます。たとえば、連絡先の詳細を取得できます。この方法については、次のレッスン連絡先の詳細を取得するをご覧ください。

検索ユーザー インターフェースについて詳しくは、API ガイド検索インターフェースを作成するをご覧ください。

このレッスンの残りのセクションでは、連絡先プロバイダで連絡先を見つけるその他の方法を示します。

特定の種類のデータで連絡先を照合する

この手法では、照合するデータの種類を指定できます。このようなクエリの具体的な例として、名前による取得が挙げられます。ただし、このようなクエリは、連絡先に関連付けられているすべての種類の詳細データに対して実行できます。たとえば、特定の郵便番号を持つ連絡先を取得できます。この場合、検索文字列は郵便番号行に格納されているデータと一致する必要があります。

このタイプの取得を実装するには、前のセクションで示した以下のコードを最初に実装します。

  • プロバイダの読み取り権限をリクエストする。
  • ListView とアイテム レイアウトを定義する。
  • 連絡先のリストを表示するフラグメントを定義する。
  • グローバル変数を定義する。
  • フラグメントを初期化する。
  • ListView の CursorAdapter を設定する。
  • 選択した連絡先リスナーを設定する。
  • Cursor 列のインデックスの定数を定義する。

    別のテーブルからデータを取得しても、プロジェクションの列の順序は同じであるため、Cursor に同じインデックスを使用できます。

  • onItemClick() メソッドを定義する。
  • ローダを初期化する。
  • onLoadFinished() と onLoaderReset() を実装する。

次の手順では、検索文字列を特定の種類の詳細データと照合して結果を表示するために必要な追加コードを示します。

データの種類とテーブルを選択する

特定の種類の詳細データを検索するには、そのカスタム MIME タイプの値を確認する必要があります。データの種類ごとに、一意の MIME タイプの値があります。この値は、データの種類に関連付けられている ContactsContract.CommonDataKinds のサブクラスにある定数 CONTENT_ITEM_TYPE で定義されます。サブクラスにはデータの種類を示す名前が付けられています。たとえば、メールデータのサブクラスは ContactsContract.CommonDataKinds.Email です。メールデータのカスタム MIME タイプは定数 Email.CONTENT_ITEM_TYPE によって定義されます。

検索には ContactsContract.Data テーブルを使用します。プロジェクション、選択句、ソート順に必要なすべての定数は、このテーブルによって定義または継承されます。

プロジェクションを定義する

プロジェクションを定義するには、ContactsContract.Data または継承元のクラスで定義されている 1 つ以上の列を選択します。連絡先プロバイダは、行を返す前に ContactsContract.Data とその他のテーブル間で暗黙的な結合を行います。次に例を示します。

Kotlin

    @SuppressLint("InlinedApi")
    private val PROJECTION: Array<out String> = arrayOf(
            /*
             * The detail data row ID. To make a ListView work,
             * this column is required.
             */
            ContactsContract.Data._ID,
            // The primary display name
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
                ContactsContract.Data.DISPLAY_NAME_PRIMARY
            else
                ContactsContract.Data.DISPLAY_NAME,
            // The contact's _ID, to construct a content URI
            ContactsContract.Data.CONTACT_ID,
            // The contact's LOOKUP_KEY, to construct a content URI
            ContactsContract.Data.LOOKUP_KEY
    )
    

Java

        @SuppressLint("InlinedApi")
        private static final String[] PROJECTION =
            {
                /*
                 * The detail data row ID. To make a ListView work,
                 * this column is required.
                 */
                ContactsContract.Data._ID,
                // The primary display name
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                        ContactsContract.Data.DISPLAY_NAME_PRIMARY :
                        ContactsContract.Data.DISPLAY_NAME,
                // The contact's _ID, to construct a content URI
                ContactsContract.Data.CONTACT_ID,
                // The contact's LOOKUP_KEY, to construct a content URI
                ContactsContract.Data.LOOKUP_KEY // A permanent link to the contact
            };
    

検索条件を定義する

特定の種類のデータ内で文字列を検索するには、以下のものから選択句を作成します。

  • 検索文字列を含む列の名前。この名前はデータ型によって異なるため、データの種類に対応する ContactsContract.CommonDataKinds のサブクラスを見つけ、そのサブクラスから列名を選択する必要があります。たとえば、メールアドレスを検索するには Email.ADDRESS 列を使用します。
  • 選択句内で「?」文字として表記される検索文字列そのもの。
  • カスタム MIME タイプの値を含む列の名前。この名前は常に Data.MIMETYPE です。
  • データの種類のカスタム MIME タイプの値。前述のとおり、これは ContactsContract.CommonDataKinds サブクラス内にある定数 CONTENT_ITEM_TYPE です。たとえば、メールデータの MIME タイプの値は Email.CONTENT_ITEM_TYPE です。この値は単一引用符で囲みます(定数の最初と最後に「'」(単一引用符)を連結します)。そうしないと、プロバイダはこの値を文字列値ではなく変数名として解釈します。ユーザーが指定した値ではなく定数を使用するため、この値にプレースホルダを使用する必要はありません。

次に例を示します。

Kotlin

    /*
     * Constructs search criteria from the search string
     * and email MIME type
     */
    private val SELECTION: String =
            /*
             * Searches for an email address
             * that matches the search string
             */
            "${Email.ADDRESS} LIKE ? AND " +
            /*
             * Searches for a MIME type that matches
             * the value of the constant
             * Email.CONTENT_ITEM_TYPE. Note the
             * single quotes surrounding Email.CONTENT_ITEM_TYPE.
             */
            "${ContactsContract.Data.MIMETYPE } = '${Email.CONTENT_ITEM_TYPE}'"
    

Java

        /*
         * Constructs search criteria from the search string
         * and email MIME type
         */
        private static final String SELECTION =
                /*
                 * Searches for an email address
                 * that matches the search string
                 */
                Email.ADDRESS + " LIKE ? " + "AND " +
                /*
                 * Searches for a MIME type that matches
                 * the value of the constant
                 * Email.CONTENT_ITEM_TYPE. Note the
                 * single quotes surrounding Email.CONTENT_ITEM_TYPE.
                 */
                ContactsContract.Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
    

次に、選択引数を含む変数を定義します。

Kotlin

        private var searchString: String? = null
        private val selectionArgs: Array<String> = arrayOf("")
    

Java

        String searchString;
        String[] selectionArgs = { "" };
    

onCreateLoader() を実装する

必要なデータとその検索方法を指定したので、onCreateLoader() を実装してクエリを定義します。プロジェクション、選択したテキスト表現、選択配列を引数として使用して、このメソッドから新しい CursorLoader を返します。コンテンツ URI には Data.CONTENT_URI を使用します。次に例を示します。

Kotlin

        override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
            // OPTIONAL: Makes search string into pattern
            searchString = "%$mSearchString%"

            searchString?.also {
                // Puts the search string into the selection criteria
                selectionArgs[0] = it
            }
            // Starts the query
            return activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        null
                )
            } ?: throw IllegalStateException()
        }
    

Java

    @Override
        public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
            // OPTIONAL: Makes search string into pattern
            searchString = "%" + searchString + "%";
            // Puts the search string into the selection criteria
            selectionArgs[0] = searchString;
            // Starts the query
            return new CursorLoader(
                    getActivity(),
                    Data.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            );
        }
    

これらのコード スニペットは、特定の種類の詳細データに基づく単純な逆引き参照をベースとしています。これは、アプリがメールなどの特定の種類のデータに焦点を当てており、ユーザーがデータに関連付けられた名前を取得できるようにする場合に使用するのに最適な手法です。

すべての種類のデータで連絡先を照合する

すべての種類のデータに基づいて連絡先を取得すると、名前、メールアドレス、住所、電話番号などのデータが検索文字列と一致する場合に連絡先が返されます。これにより、幅広い検索結果が得られます。たとえば、検索文字列が「Doe」の場合、すべての種類のデータを検索すると、連絡先「John Doe」が返されるほか、住所に「Doe Street」が含まれる連絡先も返されます。

このタイプの取得を実装するには、前のセクションで示した以下のコードを最初に実装します。

  • プロバイダの読み取り権限をリクエストする。
  • ListView とアイテム レイアウトを定義する。
  • 連絡先のリストを表示するフラグメントを定義する。
  • グローバル変数を定義する。
  • フラグメントを初期化する。
  • ListView の CursorAdapter を設定する。
  • 選択した連絡先リスナーを設定する。
  • プロジェクションを定義する。
  • Cursor 列のインデックスの定数を定義する。

    このタイプの取得では、連絡先を名前で照合して結果を一覧表示するで使用したのと同じテーブルを使用します。列のインデックスも同じものを使用します。

  • onItemClick() メソッドを定義する。
  • ローダを初期化する。
  • onLoadFinished() と onLoaderReset() を実装する。

次の手順では、検索文字列をすべての種類のデータと照合して結果を表示するために必要な追加コードを示します。

選択基準を削除する

SELECTION 定数または mSelectionArgs 変数を定義しないでください。これらは、このタイプの取得では使用されません。

onCreateLoader() を実装する

onCreateLoader() メソッドを実装して、新しい CursorLoader を返します。検索文字列をパターンに変換する必要はありません。連絡先プロバイダによって自動的に変換されます。Contacts.CONTENT_FILTER_URI をベース URI として使用し、Uri.withAppendedPath() を呼び出して検索文字列を付加します。この URI を使用すると、次の例に示すように、すべてのデータの種類の検索が自動的にトリガーされます。

Kotlin

        override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
            /*
             * Appends the search string to the base URI. Always
             * encode search strings to ensure they're in proper
             * format.
             */
            val contentUri: Uri = Uri.withAppendedPath(
                    ContactsContract.Contacts.CONTENT_FILTER_URI,
                    Uri.encode(searchString)
            )
            // Starts the query
            return activity?.let {
                CursorLoader(
                        it,
                        contentUri,
                        PROJECTION2,
                        null,
                        null,
                        null
                )
            } ?: throw IllegalStateException()
        }
    

Java

        @Override
        public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
            /*
             * Appends the search string to the base URI. Always
             * encode search strings to ensure they're in proper
             * format.
             */
            Uri contentUri = Uri.withAppendedPath(
                    Contacts.CONTENT_FILTER_URI,
                    Uri.encode(searchString));
            // Starts the query
            return new CursorLoader(
                    getActivity(),
                    contentUri,
                    PROJECTION,
                    null,
                    null,
                    null
            );
        }
    

これらのコード スニペットは、連絡先プロバイダの広範な検索を行うアプリに基づいています。 この手法は、アプリ People の連絡先リスト画面に似た機能を実装するアプリで役立ちます。