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

このレッスンでは、次の方法を使用して、検索文字列の全部または一部にデータが一致する連絡先のリストを取得する方法を説明します。

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

注: このレッスンのすべての例では、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 全体を定義するメイン レイアウト ファイルと、ListView の 1 行を定義するアイテム レイアウト ファイルが必要です。たとえば、次の 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 を使用してリストを表示し、2 つ目の Fragment を使用してユーザーがリストから選択した連絡先の詳細を表示できます。この方法を使用すると、このレッスンで紹介した手法の 1 つと、 連絡先の詳細を取得するで紹介した手法を組み合わせることができます。

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 警告が生成されます。この警告をオフにするには、FROM_COLUMNS の定義の前にアノテーション @SuppressLint("InlinedApi") を追加します。

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

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._ID 列は SimpleCursorAdapter バインディング プロセスで使用されます。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() を実装する

onCreateLoader() メソッドを実装します。このメソッドは、initLoader() を呼び出した直後にローダー フレームワークによって呼び出されます。

onCreateLoader() で検索文字列パターンを設定します。文字列をパターンに変換するには、「%」(パーセント)文字を挿入して 0 個以上の文字を示すか、「_」(アンダースコア)を挿入して 1 文字を示すか、またはその両方を表します。たとえば、パターン「%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 とアイテム レイアウトを定義します。
  • 連絡先のリストを表示するフラグメントを定義します。
  • グローバル変数を定義します。
  • Fragment を初期化する。
  • ListView の CursorAdapter を設定します。
  • 選択した連絡先リスナーを設定します。
  • Cursor 列インデックスの定数を定義します。

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

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

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

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

特定の種類の詳細データを検索するには、そのデータ型のカスタム MIME タイプの値が必要です。各データ型には、データ型に関連付けられた ContactsContract.CommonDataKinds のサブクラス内の定数 CONTENT_ITEM_TYPE によって定義される一意の MIME タイプ値があります。サブクラスには、データタイプを示す名前があります。たとえば、メールデータのサブクラスは 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 とアイテム レイアウトを定義します。
  • 連絡先のリストを表示するフラグメントを定義します。
  • グローバル変数を定義します。
  • Fragment を初期化する。
  • 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
        );
    }

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