本課程將說明如何擷取聯絡人的詳細資料,例如電子郵件地址、電話號碼等。使用者擷取聯絡人時搜尋的詳細資料。 您可以提供聯絡人的所有詳細資料,也可以只顯示特定類型的詳細資料,例如電子郵件地址。
本課程中的步驟假設您已有使用者所選聯絡人的 ContactsContract.Contacts
資料列。「擷取聯絡人名稱」課程說明如何擷取聯絡人清單。
擷取聯絡人的所有詳細資料
如要擷取聯絡人的所有詳細資料,請在 ContactsContract.Data
資料表中搜尋包含聯絡人 LOOKUP_KEY
的任何資料列。這個資料欄可在 ContactsContract.Data
資料表中使用,因為聯絡人提供者會在 ContactsContract.Contacts
資料表與 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()
來初始化載入器架構。將整數 ID 傳遞至方法,這個 ID 會傳遞至 LoaderManager.LoaderCallbacks
方法。ID 可讓您在應用程式中使用多個載入器,以便加以區分。
下列程式碼片段說明如何初始化載入器架構:
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
資料表,請使用常數 Data.CONTENT_URI
做為內容 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; ... } }
當載入器架構偵測到支援 Cursor
結果的資料已變更時,系統會叫用 onLoaderReset()
方法。此時,請將 Cursor
的所有現有參照設為空值,藉此移除這些參照。否則載入器架構不會刪除舊的 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
值。 - 排序順序
-
您只會選取單一詳細資料類型,因此請勿按
Data.MIMETYPE
將傳回的Cursor
分組。
我們會在以下各節說明這些修改內容。
定義投影
定義要擷取的資料欄,並針對資料類型使用 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.CommonDataKinds.Email
類別中定義的資料欄名稱,而非 ContactsContract.Data
類別中定義的資料欄名稱。使用電子郵件專屬的資料欄名稱可讓程式碼更容易閱讀。
在投影中,您也可以使用 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 ";