好的,经过大量挖掘,我找到了我认为的答案。我发现的解决方案根据您使用的 Android API 级别而有所不同。但是,它们一点也不漂亮,所以如果有更好的解决方案,我很想知道。
在任何情况下,第一步都是通过查询 Intent.ACTION_PICK 返回的 URI 来获取联系人的 ID。当我们在这里时,我们还应该获取显示名称,以及表示联系人是否有电话号码的字符串。(现代解决方案不需要它们,但旧解决方案需要它们。)
String id, name, phone, hasPhone;
int idx;
Cursor cursor = getContentResolver().query(contactUri, null, null, null, null);
if (cursor.moveToFirst()) {
idx = cursor.getColumnIndex(ContactsContract.Contacts._ID);
id = cursor.getString(idx);
idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
name = cursor.getString(idx);
idx = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
hasPhone = cursor.getString(idx);
}
作为记录,从这个URI返回的列是ContactsContract.Profile类中的常量表示的大部分字段(包括从其他接口继承的常量)。不包括 PHOTO_FILE_ID、PHOTO_THUMBNAIL_URI 或 PHOTO_URI(但包括 PHOTO_ID )。
现在我们有了 ID,我们需要获取相关数据。第一个(也是最简单的)解决方案是查询实体。实体查询一次检索联系人或原始联系人的所有联系人数据。每行代表一个原始联系人,使用ContactsContract.Contacts.Entity中的常量访问。通常您只关心 RAW_CONTACT_ID、DATA1 和 MIMETYPE。但是,如果您想分别使用名字和姓氏,Name MIME 类型将名字保存在 DATA2 中,将姓氏保存在 DATA3 中。
通过将 MIMETYPE 列与ContactsContract.CommonDataKinds常量匹配来加载变量;例如,电子邮件 MIME 类型位于ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE中。
// Build the Entity URI.
Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon();
b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
URI contactUri = b.build();
// Create the projection (SQL fields) and sort order.
String[] projection = {
ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
ContactsContract.Contacts.Entity.DATA1,
ContactsContract.Contacts.Entity.MIMETYPE };
String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC";
cursor = getContentResolver().query(contactUri, projection, null, null, sortOrder);
String mime;
int mimeIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.MIMETYPE);
int dataIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.DATA1);
if (cursor.moveToFirst()) {
do {
mime = cursor.getString(mimeIdx);
if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
email = cursor.getString(dataIdx);
}
if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
phone = cursor.getString(dataIdx);
}
// ...etc.
} while (cursor.moveToNext());
}
不幸的是,在 API 11(Android 3.0,Honeycomb)中没有引入实体,这意味着该代码与市场上大约 65% 的 Android 设备不兼容(截至撰写本文时)。如果你尝试一下,你会从 URI 中得到一个IllegalArgumentException 。
第二种解决方案是构建一个查询字符串,并为您要使用的每种数据类型进行一次查询:
// Get phone number - if they have one
if ("1".equalsIgnoreCase(hasPhone)) {
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "+ id,
null, null);
if (cursor.moveToFirst()) {
colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
phone = cursor.getString(colIdx);
}
cursor.close();
}
// Get email address
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + id,
null, null);
if (cursor.moveToFirst()) {
colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
email = cursor.getString(colIdx);
}
cursor.close();
// ...etc.
显然,这种方式会导致大量单独的数据库查询,因此出于效率原因不推荐使用。
我想出的解决方案是尝试使用实体查询的版本,捕获 IllegalArgumentException,并将遗留代码放在 catch 块中:
try {
// Build the Entity URI.
Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon();
b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
// ...etc...
} catch (IllegalArgumentException e) {
// Get phone number - if they have one
if ("1".equalsIgnoreCase(hasPhone)) {
// ...etc...
} finally {
// If you want to display the info in GUI objects, put the code here
}
我希望这可以帮助别人。而且,再一次,如果有更好的方法可以做到这一点,我会全力以赴。