このレッスンでは、連絡先の詳細データ(メールアドレスや電話番号など)を取得する方法について説明します。ユーザーが連絡先を取得する際に確認する必要がある詳細情報です。 連絡先のすべての詳細情報をユーザーに表示することも、メールアドレスなど、特定の種類の詳細情報だけを表示することもできます。
このレッスンの手順では、ユーザーが選択した連絡先に対応する ContactsContract.Contacts
行がすでに存在することを前提としています。連絡先名の取得のレッスンでは、連絡先のリストを取得する方法を示します。
連絡先のすべての詳細情報を取得する
連絡先のすべての詳細を取得するには、ContactsContract.Data
テーブルで連絡先の LOOKUP_KEY
を含む行を検索します。連絡先プロバイダが ContactsContract.Contacts
テーブルと ContactsContract.Data
テーブルを暗黙的に結合するため、この列を ContactsContract.Data
テーブルで使用できます。LOOKUP_KEY
列について詳しくは、連絡先名の取得のレッスンをご覧ください。
注: 連絡先のすべての詳細を取得すると、ContactsContract.Data
テーブル内のすべての列を取得する必要があるため、デバイスのパフォーマンスが低下します。この手法を使用する前に、パフォーマンスへの影響を考慮してください。
権限をリクエストする
連絡先プロバイダから読み取るには、アプリに READ_CONTACTS
権限が必要です。この権限をリクエストするには、
<manifest>
の次の子要素をマニフェスト ファイルに追加します。
<uses-permission android:name="android.permission.READ_CONTACTS" />
射影をセットアップする
行に含まれるデータ型に応じて、使用する列の数は変わります。また、データはデータ型に応じて異なる列にあります。利用可能なすべてのデータ型で可能な列をすべて取得するには、すべての列名を射影に追加する必要があります。結果 Cursor
を ListView
にバインドする場合は、必ず Data._ID
を取得します。そうしないと、バインディングが機能しません。また、Data.MIMETYPE
も取得して、取得する各行のデータ型を特定できるようにします。次に例を示します。
Kotlin
private val PROJECTION: Array<out String> = arrayOf( ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 )
Java
private static final String[] PROJECTION = { ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 };
この射影は、ContactsContract.Data
クラスで定義された列名を使用して、ContactsContract.Data
テーブルの行のすべての列を取得します。
必要に応じて、ContactsContract.Data
クラスで定義されているか、それによって継承される他の列定数を使用することもできます。ただし、SYNC1
列から SYNC4
列は同期アダプターによって使用されることを想定しているため、そのデータは有用ではありません。
選択基準を定義する
選択句の定数、選択引数を保持する配列、選択値を保持する変数を定義します。Contacts.LOOKUP_KEY
列を使用して連絡先を見つけます。次に例を示します。
Kotlin
// Defines the selection clause private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("") /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private var lookupKey: String? = null
Java
// Defines the selection clause private static final String SELECTION = Data.LOOKUP_KEY + " = ?"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" }; /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private lateinit var lookupKey: String
選択テキスト式でプレースホルダとして「?」を使用すると、SQL コンパイルではなく、バインディングによって結果の検索が生成されます。このアプローチにより、悪意のある SQL インジェクションの可能性が排除されます。
ソート順を定義する
結果 Cursor
内のソート順を定義します。特定のデータ型のすべての行をまとめて保持するには、Data.MIMETYPE
で並べ替えます。このクエリ引数は、メールのすべての行、電話番号のすべての行などのようにグループ化します。次に例を示します。
Kotlin
/* * Defines a string that specifies a sort order of MIME type */ private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
Java
/* * Defines a string that specifies a sort order of MIME type */ private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
注: 一部のデータ型はサブタイプを使用しないため、サブタイプで並べ替えることはできません。代わりに、返された Cursor
を反復処理し、現在の行のデータ型を特定して、サブタイプを使用する行のデータを保存する必要があります。カーソルの読み取りが完了したら、各データ型をサブタイプで並べ替えて、結果を表示できます。
ローダーを初期化する
連絡先プロバイダ(および他のすべてのコンテンツ プロバイダ)からの取得は、常にバックグラウンド スレッドで実行します。LoaderManager
クラスと LoaderManager.LoaderCallbacks
インターフェースで定義されたローダ フレームワークを使用して、バックグラウンド取得を行います。
行を取得する準備ができたら、initLoader()
を呼び出してローダー フレームワークを初期化します。整数の識別子をメソッドに渡します。この識別子は LoaderManager.LoaderCallbacks
メソッドに渡されます。この識別子により、アプリ内で複数のローダーを区別できるようにすることで、複数のローダーを使用できます。
次のスニペットは、ローダー フレームワークを初期化する方法を示しています。
Kotlin
// Defines a constant that identifies the loader private const val DETAILS_QUERY_ID: Int = 0 class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Initializes the loader framework loaderManager.initLoader(DETAILS_QUERY_ID, null, this)
Java
public class DetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a constant that identifies the loader static int DETAILS_QUERY_ID = 0; ... @Override public void onCreate(Bundle savedInstanceState) { ... // Initializes the loader framework getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
onCreateLoader() を実装する
onCreateLoader()
メソッドを実装します。このメソッドは、initLoader()
を呼び出した直後にローダー フレームワークによって呼び出されます。このメソッドから CursorLoader
を返します。ContactsContract.Data
テーブルを検索しているため、コンテンツ URI として定数 Data.CONTENT_URI
を使用します。次に例を示します。
Kotlin
override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { // Choose the proper action mLoader = when(loaderId) { DETAILS_QUERY_ID -> { // Assigns the selection parameter selectionArgs[0] = lookupKey // Starts the query activity?.let { CursorLoader( it, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ) } } ... } return mLoader }
Java
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { // Choose the proper action switch (loaderId) { case DETAILS_QUERY_ID: // Assigns the selection parameter selectionArgs[0] = lookupKey; // Starts the query CursorLoader mLoader = new CursorLoader( getActivity(), ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ); }
onLoadFinished() と onLoaderReset() を実装する
onLoadFinished()
メソッドを実装します。連絡先プロバイダがクエリの結果を返すと、ローダー フレームワークは onLoadFinished()
を呼び出します。次に例を示します。
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { when(loader.id) { DETAILS_QUERY_ID -> { /* * Process the resulting Cursor here. */ } ... } }
Java
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * Process the resulting Cursor here. */ } break; ... } }
メソッド onLoaderReset()
は、結果 Cursor
をサポートするデータが変更されたことをローダー フレームワークが検出すると呼び出されます。この時点で、Cursor
への既存の参照を null に設定して削除します。そうしないと、ローダー フレームワークによって古い Cursor
が破棄されず、メモリリークが発生します。次に例を示します。
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { when (loader.id) { DETAILS_QUERY_ID -> { /* * If you have current references to the Cursor, * remove them here. */ } ... } }
Java
@Override public void onLoaderReset(Loader<Cursor> loader) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * If you have current references to the Cursor, * remove them here. */ } break; }
連絡先の特定の詳細情報を取得する
連絡先の特定のデータ型(すべてのメールなど)を取得する場合は、すべての詳細情報を取得する場合と同じパターンを使用します。連絡先のすべての詳細を取得するで説明されているコードに対して、以下の変更を行う必要があります。
- 予測
-
射影を変更して、データ型に固有の列を取得します。また、データ型に対応する
ContactsContract.CommonDataKinds
サブクラスで定義された列名定数を使用するように、射影を変更します。 - 選択
- 選択テキストを変更して、データ型に固有の
MIMETYPE
値を検索します。 - 並べ替え順
-
選択する詳細タイプは 1 つのみであるため、返された
Cursor
をData.MIMETYPE
でグループ化しないでください。
これらの変更については、次のセクションで説明します。
射影を定義する
データ型の ContactsContract.CommonDataKinds
のサブクラスで列名定数を使用して、取得する列を定義します。
Cursor
を ListView
にバインドする場合は、_ID
列を必ず取得してください。たとえば、メールデータを取得するには、次の予測を定義します。
Kotlin
private val PROJECTION: Array<String> = arrayOf( ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL )
Java
private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL };
この射影では、クラス ContactsContract.Data
で定義された列名ではなく、クラス ContactsContract.CommonDataKinds.Email
で定義された列名が使用されます。メール固有の列名を使用すると、コードが読みやすくなります。
射影では、ContactsContract.CommonDataKinds
サブクラスで定義された他の列を使用することもできます。
選択基準を定義する
特定の連絡先の LOOKUP_KEY
と必要な詳細情報の Data.MIMETYPE
の行を取得する検索テキスト式を定義します。MIMETYPE
値は一重引用符で囲みます。そのためには、定数の先頭と末尾に「'
」(一重引用符)を連結します。そうしないと、プロバイダは定数を文字列値ではなく変数名と解釈します。ユーザー指定の値ではなく定数を使用しているため、この値にプレースホルダを使用する必要はありません。次に例を示します。
Kotlin
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private const val SELECTION = "${ContactsContract.Data.LOOKUP_KEY} = ? AND " + "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("")
Java
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private static final String SELECTION = Data.LOOKUP_KEY + " = ?" + " AND " + Data.MIMETYPE + " = " + "'" + Email.CONTENT_ITEM_TYPE + "'"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" };
ソート順を定義する
返された Cursor
のソート順を定義します。ここでは特定のデータ型を取得するため、MIMETYPE
での並べ替えを省略します。代わりに、検索する詳細データのタイプにサブタイプが含まれている場合は、そのサブタイプに基づいて並べ替えます。たとえば、メールデータの場合は Email.TYPE
で並べ替えることができます。
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";