一、背景
传统上,应用在读取用户存储在设备的联系人数据时,通常需要通过权限弹窗,向用户申请读取联系人权限android.permission.READ_CONTACTS,由用户主动授权后,应用才能使用ContentProvider API读取到联系人数据。该方案存在诸多痛点,举例如下:
- 应用一般不需要读取完整的联系人数据,但申请并获得读取联系人权限,将获得读取完整联系人数据的能力,容易引起监管、用户的质疑,增加应用澄清的负担。
- 与此同时,用户出于隐私考虑,可能会拒绝授权,进而导致应用相关功能不可用,影响应用的用户体验。
- 此外,用户如果多次误拒绝授权,但仍然希望使用联系人相关的应用功能,应用将需引导用户,跳转到设置中进行手动授权,用户才能继续正常使用联系人相关应用功能,影响应用的用户体验。
为解决上述问题,联系人Picker应运而生。应用可通过接入联系人Picker,弹出系统级的联系人Picker选择界面,由用户手动选择需要的联系人信息,授权给应用读取,具有如下显著优势:
- 应用不再需要申请联系人权限,解决用户拒绝授权导致应用功能不可用问题。
- 应用只能读取用户选择的联系人数据,满足用户最小化授权的需求,同时减少应用的合规压力。
二、功能介绍
1. 应用场景介绍
应用通过接入联系人Picker,可在联系人相关功能中(如图一),拉起系统级的联系人Picker选择界面(如图二)。用户可在联系人Picker界面中执行选择、搜索等(如图二、图三),用户选择希望授权的联系人后,将返回应用界面。应用即可读取到用户选择并授权给应用的联系人数据(如图四)。
图一:

图二:

图三:

图四:

2. 联系人Picker功能介绍
联系人Picker整体支持如下功能:
2.1 单选
- 电话号码信息单选:用户可选择一个电话号码信息,授权给应用,应用可读取到姓名、电话号码等信息,如图五。
- 邮箱地址信息单选:用户可选择一个邮箱地址信息,授权给应用,应用可读取到姓名、邮箱地址等信息,如图六。
- 联系人信息单选:用户可选择一个联系人信息,授权给应用,应用可读取到姓名等信息,如图七。
对应联系人Picker界面如下:
图五:

图六:

图七:

2.2 多选
- 电话号码信息的多选:用户可选择一个或多个(最多不超过50个)电话号码信息,授权给应用,应用可逐个读取到姓名、电话号码等信息,如图八。
- 邮箱地址信息的多选:用户可选择一个或多个(最多不超过50个)邮箱地址信息,授权给应用,应用可逐个读取到姓名、邮箱地址等信息,如图九。
- 联系人信息的多选:用户可选择一个或多个(最多不超过50个)联系人信息,授权给应用,应用可逐个读取到姓名等信息,如图十。
对应联系人Picker界面如下:
图八:

图九:

图十:

三、 适用范围
当前支持联系人Picker的操作系统版本如下:
- HyperOS 3(基于Android 16)及以上
- ColorOS 16(基于Android 16)及以上
- OriginOS 6(基于Android 16)及以上
- MagicOS 10(基于Android 16)及以上
四、使用说明
以下详细说明适配联系人Picker的接口。
1. Intent构造参数
应用按下述Intent参数说明,构造Intent,使用startActivityForResult(Intent intent, int requestCode),即可拉起联系人Picker界面。
1.1 action
构造Intent,应用需传入action参数,必选。
| 构造参数 | action |
| 参数类型 | String |
| 构造API | new Intent(String action) |
| new Intent(String action, Uri data) | |
| Intent.setAction(String action) | |
| 联系人Picker取值 | Intent.ACTION_PICK="android.intent.action.PICK" |
调用示例:
Intent intent = new Intent(Intent.ACTION_PICK);1.2 Type
应用还需要传入type或data参数,二选一,用于指定联系人数据类型,必须指定,推荐使用type参数。
| 构造参数 | type |
| 参数类型 | String |
| 构造API | Intent.setType(String type) |
| Intent.setDataAndType(Uri data, String type) | |
| 联系人Picker取值 | 联系人电话号码 ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE |
| 联系人邮箱 ContactsContract.CommonDataKinds.Email.CONTENT_TYPE | |
| 联系人 ContactsContract.Contacts.CONTENT_TYPE |
调用示例:
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);1.3 data
应用如不传入type参数,则需传入data参数。
备注:type与data参数二选一,用于指定联系人数据类型,必须指定,推荐使用type参数。
| 构造参数 | data |
| 参数类型 | Uri |
| 构造API | Intent.setData(Uri data) |
| new Intent(String action, Uri data) | |
| Intent.setDataAndType(Uri data, String type) | |
| 联系人Picker取值 | 联系人电话号码 ContactsContract.CommonDataKinds.Phone.CONTENT_URI |
| 联系人邮箱 ContactsContract.CommonDataKinds.Email.CONTENT_URI | |
| 联系人 ContactsContract.Contacts.CONTENT_URI |
调用示例:
intent.setData(ContactsContract.CommonDataKinds.Phone.CONTENT_URI)1.4 Extras: EXTRA_ALLOW_MULTIPLE
应用可选传入是否多选的EXTRA_ALLOW_MULTIPLE参数,如不传入,则默认为单选。
| 构造参数 | EXTRA_ALLOW_MULTIPLE |
| 参数类型 | boolean |
| 构造API | Intent.putExtra(String name, boolean value) |
| 联系人Picker取值 | name参数取值:Intent.EXTRA_ALLOW_MULTIPLE name参数说明:是否多选 |
| value参数取值范围:true/false value参数说明: · true对应多选, · false对应单选 默认值:false,不设置默认为false,单选 |
使用示例:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)2. Intent返回值
应用通过上述Intent构造参数构造出目标intent,加上大于0的requestCode,通过startActivityForResult(Intent intent, int requestCode)即可拉起系统联系人Picker界面。
用户可在联系人Picker界面操作,选择联系人数据,授权给应用。
用户选择后,应用可在onActivityResult(Int requestCode, Int resultCode, Intent data)回调中获取requestCode以及resultCode结果代码、data数据,其中data中包含联系人数据对应的Uri。
2.1 requestCode:
在onActivityResult回调中,requestCode值就是传入startActivityForResult的requestCode,关联回调的结果与对应的请求。
| 返回值 | requestCode |
| 参数类型 | int |
| 取值 | 与startActivityForResult(Intent intent, int requestCode)时传入的requestCode一致,代表对应请求的回调。 |
2.2 resultCode
在onActivityResult回调中,resultCode代表请求的结果。
| 返回值 | resultCode |
| 参数类型 | int |
| 取值范围 | Activity.RESULT_OK,成功 |
| Activity.RESULT_CANCELED,被取消 |
2.3 data
在onActivityResult回调中,data包含授权给应用的联系人数据对应的URI。
| 返回值 | data |
| 参数类型 | Intent |
| 取值范围 | 单选: data.getData()拿到Uri,可通过ContentResolver.query读取数据 |
| 多选: data.getClipData()获取到ClipData实例, 可通过ClipData.getItemCount拿到返回的个数, ClipData.getItemAt(index).getUri()拿到对应index的Uri,然后通过ContentResolver.query读取数据 |
3. 通过Uri查询联系人数据
3.1 查询
通过回调,获取到联系人数据对应的Uri,即可使用getContentResolver().query(Uri)接口查询联系人数据,与使用权限读取联系人数据一致。
调用示例:
Cursor cursor = getContentResolver().query(contactUri, null, null, null, null)3.2 字段
查询到的联系人数据中包含很多字段,应用可根据需求,读取指定字段。
完整字段请参考Android开发者网站:
https://developer.android.google.cn/reference/android/provider/ContactsContract
https://developer.android.google.cn/reference/android/provider/ContactsContract.Contacts
下表展示关键的数据字段:
| 类型 | 数据名称 | 数据字段 |
| 联系人电话号码 | 姓名 | ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME |
| 联系人电话号码 | 号码 | ContactsContract.CommonDataKinds.Phone.NUMBER |
| 联系人邮箱 | 姓名 | ContactsContract.CommonDataKinds.Email.DISPLAY_NAME_PRIMARY |
| 联系人邮箱 | 邮箱地址 | ContactsContract.CommonDataKinds.Email.ADDRESS |
| 联系人 | 姓名 | ContactsContract.Contacts.DISPLAY_NAME |
调用示例:
int nameIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);String name = cursor.getString(nameIndex);int phoneIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);String phone = cursor.getString(phoneIndex);五、调用示例
1. AndroidManifest配置
1.1 移除权限配置
在支持联系人Picker的系统版本,应用不再需要申请读取联系人权限。
- 应用可移除
AndroidManiest.xml中的读取联系人权限申请
<!--<uses-permission android:name="android.permission.READ_CONTACTS" />-->- 应用也可在
AndroidManifest.xml中配置读取联系人权限最高可用的Android版本,以最高支持在Android 15(SDK版本号35)申请读取联系人权限为例
<uses-permission android:name="android.permission.READ_CONTACTS" android:maxSdkVersion="35" />1.2 增加query配置
在应用的AndroidManifest.xml增加如下配置
<queries>
<intent>
<action android:name="android.intent.action.PICK" />
<data android:mimeType="vnd.android.cursor.dir/*" />
</intent></queries>2. 判断系统是否支持
应用可通过如下方法,判断联系人Picker是否可用:
2.1 Java示例
//传入intent,是否PackageManager查询是否支持
public boolean isContactsPickerAvailable(Intent intent) {
return getPackageManager().resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY) != null;}应用可在不支持联系人Picker的操作系统,继续申请读取联系人权限。
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_SINGLE_PICK_CONTACTS);} else {
//不支持,申请权限}2.2 Kotlin示例
//传入intent,是否PackageManager查询是否支持
fun isContactsPickerAvailable(intent: Intent): Boolean {
return getPackageManager().resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY) != null;}应用可在不支持联系人Picker的操作系统,继续申请读取联系人权限。
val intent = Intent(Intent.ACTION_PICK)
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE)if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_SINGLE_PICK_CONTACTS)} else {
//不支持,申请权限}3. 单选联系人示例
以下为使用联系人Picker相关API拉起单选联系人界面,并读取用户选择的联系人数据的完整示例。
3.1 Java用法(未集成Android X场景)
以下是Java代码示例,在未集成AndroidX的场景,使用原生API。
final int REQUEST_CODE_SINGLE_PICK_CONTACTS = 1001;
public void pickSingleContact() {
//构造intent,设置action
Intent intent = new Intent(Intent.ACTION_PICK);
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_SINGLE_PICK_CONTACTS);
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission");
}}
//接收回调@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);
//校验requestCode是否与startActivityForResult传入的一致
if (requestCode == REQUEST_CODE_SINGLE_PICK_CONTACTS) {
Log.d(TAG, "resultCode:" + resultCode);
//判断结果是否成功
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
return;
}
//读取到用户选择的联系人数据的Uri
Uri contactUri = data.getData();
if (contactUri == null) {
return;
}
//通过ContentResolver.query接口从Uri读取数据
try (Cursor cursor = getContentResolver().query(contactUri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name = cursor.getString(nameIndex);
//读取手机号码字段
int phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String phone = cursor.getString(phoneIndex);
Log.d(TAG, "name:" + name + ",phone:" + phone);
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled");
}
}}3.2 Kotlin用法(未集成Android X场景)
以下是Kotlin代码示例,在未集成AndroidX的场景,使用原生API。
val REQUEST_CODE_SINGLE_PICK_CONTACTS = 1001
fun pickSingleContact() {
//构造intent,设置action
val intent = Intent(Intent.ACTION_PICK)
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE)
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_SINGLE_PICK_CONTACTS)
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission")
}}
//接收回调override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
//校验requestCode是否与startActivityForResult传入的一致
if (requestCode == REQUEST_CODE_SINGLE_PICK_CONTACTS) {
Log.d(TAG, "resultCode:" + resultCode)
//判断结果是否成功
if (resultCode == Activity.RESULT_OK) {
//读取到用户选择的联系人数据的Uri
val contactUri = data?.getData()
if (contactUri == null) {
return
}
//通过ContentResolver.query接口从Uri读取数据
getContentResolver().query(contactUri, null, null, null, null).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
val nameIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val name = cursor.getString(nameIndex)
//读取手机号码字段
val phoneIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
val phone = cursor.getString(phoneIndex)
Log.d(TAG, "name:" + name + ",phone:" + phone)
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled")
}
}}3.3 Java用法(集成Android X场景)
以下是Java代码示例,在集成androidx.activity jetpack库的场景,使用AndroidX的API。
public void pickSingleContact2() {
//构造intent,设置action
Intent intent = new Intent(Intent.ACTION_PICK);
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
contactActivityResultLauncher.launch(intent);
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission");
}}
//使用androidx.activity的ActivityResultLauncherActivityResultLauncher<Intent> contactActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
Log.d(TAG, "resultCode:" + result.getResultCode());
//判断结果是否成功
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
if (data == null) {
return;
}
//读取到用户选择的联系人数据的Uri
Uri contactUri = data.getData();
if (contactUri == null) {
return;
}
//通过ContentResolver.query接口从Uri读取数
try (Cursor cursor = getContentResolver().query(contactUri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name = cursor.getString(nameIndex);
//读取电话号码字段
int phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String phone = cursor.getString(phoneIndex);
Log.d(TAG, "name:" + name + ",phone:" + phone);
}
}
} else if (result.getResultCode() == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled");
}
}
});3.4 Kotlin用法(集成Android X场景)
以下是Kotlin代码示例,在集成androidx.activity jetpack库的场景,使用AndroidX的API。
fun pickSingleContact2() {
//构造intent,设置action
val intent = Intent(Intent.ACTION_PICK)
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
contactActivityResultLauncher.launch(intent)
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission")
}}
//使用androidx.activity的ActivityResultLauncherval contactActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()) { result ->
Log.d(TAG, "resultCode:" + result.resultCode)
//判断结果是否成功
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
//读取到用户选择的联系人数据的Uri
val contactUri = data?.getData()
if (contactUri == null)
return@registerForActivityResult
//通过ContentResolver.query接口从Uri读取数
getContentResolver().query(contactUri, null, null, null, null)
.use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
val nameIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val name = cursor.getString(nameIndex)
//读取电话号码字段
val phoneIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
val phone = cursor.getString(phoneIndex)
Log.d(TAG, "name:" + name + ",phone:" + phone)
}
}
} else if (result.resultCode == RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled")
}}4. 多选联系人示例
以下为使用联系人Picker相关API拉起多选联系人界面,并读取用户选择的联系人数据的完整示例。
4.1 Java用法(未集成Android X场景)
以下是Java代码示例,在未集成AndroidX的场景,使用原生API。
final int REQUEST_CODE_MULTIPLE_PICK_CONTACTS = 10002;
public void pickMultipleContact() {
//构造intent,设置action
Intent intent = new Intent(Intent.ACTION_PICK);
//设置intent的是否多选extra参数,指定为true,使用多选
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_MULTIPLE_PICK_CONTACTS);
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission");
}}
//接收回调@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//校验requestCode是否与startActivityForResult传入的一致
if (requestCode == REQUEST_CODE_MULTIPLE_PICK_CONTACTS) {
Log.d(TAG, "resultCode:" + resultCode);
//判断结果是否成功
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
return;
}
//从data中取出ClipData
ClipData clipData = data.getClipData();
if (clipData == null) {
return;
}
//遍历ClipData
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
if (item == null) {
continue;
}
//逐个读取用户选择的联系人数据的Uri
Uri contactUri = item.getUri();
if (contactUri == null) {
continue;
}
//通过ContentResolver.query接口从Uri读取数据
try (Cursor cursor = getContentResolver().query(contactUri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name = cursor.getString(nameIndex);
//读取手机号码字段
int phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String phone = cursor.getString(phoneIndex);
Log.d(TAG, "name:" + name + ",phone:" + phone);
}
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled");
}
}}4.2 Kotlin用法(未集成Android X场景)
以下是Kotlin代码示例,在未集成AndroidX的场景,使用原生API。
val REQUEST_CODE_MULTIPLE_PICK_CONTACTS = 10002
fun pickMultipleContact() {
//构造intent,设置action
val intent = Intent(Intent.ACTION_PICK)
//设置intent的是否多选extra参数,指定为true,使用多选
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE)
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
startActivityForResult(intent, REQUEST_CODE_MULTIPLE_PICK_CONTACTS)
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission")
}}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_MULTIPLE_PICK_CONTACTS) {
Log.d(TAG, "resultCode:" + resultCode)
//判断结果是否成功
if (resultCode == Activity.RESULT_OK) {
//从data中取出ClipData
val clipData = data?.getClipData()
if (clipData == null) {
return
}
//遍历ClipData
for (i in 0..<clipData.getItemCount()) {
val item = clipData.getItemAt(i)
//逐个读取用户选择的联系人数据的Uri
val contactUri = item.getUri()
if (contactUri == null) {
continue
}
//通过ContentResolver.query接口从Uri读取数据
getContentResolver().query(contactUri, null, null, null, null).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
val nameIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val name = cursor.getString(nameIndex)
//读取手机号码字段
val phoneIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
val phone = cursor.getString(phoneIndex)
Log.d(TAG, "name:" + name + ",phone:" + phone)
}
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled")
}
}}4.3 Java用法(集成Android X场景)
以下是Java代码示例,在集成androidx.activity jetpack库的场景,使用AndroidX的API。
public void pickMultiContact2() {
//构造intent,设置action
Intent intent = new Intent(Intent.ACTION_PICK);
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
//设置intent的是否多选extra参数,指定为true,使用多选
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
multiContactActivityResultLauncher.launch(intent);
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission");
}}
//使用androidx.activity的ActivityResultLauncherActivityResultLauncher<Intent> multiContactActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
Log.d(TAG, "resultCode:" + result.getResultCode());
//判断结果是否成功
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
if (data == null) {
return;
}
//从data中取出ClipData
ClipData clipData = data.getClipData();
if (clipData == null) {
return;
}
//遍历ClipData
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
if (item == null) {
continue;
}
//逐个读取用户选择的联系人数据的Uri
Uri contactUri = item.getUri();
if (contactUri == null) {
continue;
}
//通过ContentResolver.query接口从Uri读取数据
try (Cursor cursor = getContentResolver().query(contactUri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name = cursor.getString(nameIndex);
//读取电话号码字段
int phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String phone = cursor.getString(phoneIndex);
Log.d(TAG, "name:" + name + ",phone:" + phone);
}
}
}
} else if (result.getResultCode() == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled");
}
}
});4.4 Kotlin用法(集成Android X场景)
以下是Kotlin代码示例,在集成androidx.activity jetpack库的场景,使用AndroidX的API。
fun pickMultiContact2() {
//构造intent,设置action
val intent = Intent(Intent.ACTION_PICK)
//设置intent的type参数,指定为电话号码类型
intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE)
//设置intent的是否多选extra参数,指定为true,使用多选
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
//判断系统是否支持联系人Picker
if (isContactsPickerAvailable(intent)) {
//支持,拉起联系人Picker
multiContactActivityResultLauncher.launch(intent)
} else {
//不支持,申请权限
Log.d(TAG, "Not Support, request permission")
}}
//使用androidx.activity的ActivityResultLauncherval multiContactActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()) { result ->
Log.d(TAG, "resultCode:" + result.resultCode)
//判断结果是否成功
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
//从data中取出ClipData
val clipData = data?.getClipData()
if (clipData == null) {
return@registerForActivityResult
}
//遍历ClipData
for (i in 0..<clipData.getItemCount()) {
val item = clipData.getItemAt(i)
//逐个读取用户选择的联系人数据的Uri
val contactUri = item.getUri()
if (contactUri == null) {
continue
}
//通过ContentResolver.query接口从Uri读取数据
getContentResolver().query(contactUri, null, null, null, null)
.use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
//读取姓名字段
val nameIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val name = cursor.getString(nameIndex)
//读取电话号码字段
val phoneIndex =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
val phone = cursor.getString(phoneIndex)
Log.d(TAG, "name:" + name + ",phone:" + phone)
}
}
}
} else if (result.resultCode == Activity.RESULT_CANCELED) {
//用户在选择过程中取消
Log.d(TAG, "user canceled")
}}六、 Android开发者文档参考
- Intent相关
联系人提供程序:https://developer.android.google.cn/identity/providers/contacts-provider?hl=zh-cn#Intents
常见intent:https://developer.android.google.cn/guide/components/intents-common?hl=zh-cn#PickContact
- ActivityResult相关
startActivityForResult:https://developer.android.google.cn/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int)
从Activity获取结果:https://developer.android.com/training/basics/intents/result?hl=zh-cn
- Provider数据查询相关
ContentResolver.query查询:https://developer.android.google.cn/reference/android/content/ContentResolver
- 联系人数据相关字段
ContactsContract:https://developer.android.google.cn/reference/android/provider/ContactsContract
ContactsContract.CommonDataKinds.Phone:https://developer.android.google.cn/reference/android/provider/ContactsContract.CommonDataKinds.Phone
ContactsContract.CommonDataKinds.Email:https://developer.android.google.cn/reference/android/provider/ContactsContract.CommonDataKinds.Email
ContactsContract.Contacts:https://developer.android.google.cn/reference/android/provider/ContactsContract.Contacts
该适配指南与金标联盟官网适配指南一致,您也可查阅金标联盟官网中公示适配指南。
金标联盟官方文档地址:https://www.itgsa.com/doc/6631953378231296