本课程介绍了如何检索联系人的详细数据,例如电子邮件地址、电话号码 等等用户在检索联系人时要查找的就是这些详细数据。 您可以为用户提供联系人的所有详细信息,也可以仅显示特定类型的详细信息(如电子邮件地址)。
本课程中的步骤假定您已有用户所选联系人的 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()
。将
整数标识符;系统会将此标识符传递到
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
表,因此请使用常量 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
的所有现有引用设置为 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
值,是 作为数据类型 - 排序顺序
-
由于您只选择一种详情类型,因此请勿将返回的
《
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.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 ";