You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:实现支持联系人导入与展示的多选RecyclerView

实现联系人多选RecyclerView完整步骤

1. 申请联系人权限

先在AndroidManifest.xml中声明权限:

<uses-permission android:name="android.permission.READ_CONTACTS" />

再在Activity/Fragment中动态申请权限:

// Kotlin示例
private val requestContactsPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
    if (granted) {
        loadContacts()
    } else {
        // 提示用户开启权限
    }
}

// 在合适时机调用(比如onCreate)
requestContactsPermission.launch(Manifest.permission.READ_CONTACTS)

2. 读取手机联系人数据

先封装联系人数据类:

data class Contact(
    val id: String,
    val name: String,
    val phoneNumber: String
)

然后实现读取联系人的方法:

private fun loadContacts(): List<Contact> {
    val contacts = mutableListOf<Contact>()
    val contentResolver = contentResolver
    val projection = arrayOf(
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
    )
    val cursor = contentResolver.query(
        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
        projection,
        null,
        null,
        "${ContactsContract.Contacts.DISPLAY_NAME} ASC"
    )
    cursor?.use {
        while (it.moveToNext()) {
            val id = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
            val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
            val phone = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
                .replace("\\s".toRegex(), "") // 去除手机号空格
            contacts.add(Contact(id, name, phone))
        }
    }
    return contacts
}

3. 改造RecyclerView多选逻辑

基于你参考的TextView版逻辑,扩展为联系人多选:

  • 用单独集合管理选中的联系人ID,不污染原始数据模型
  • 点击Item切换选中状态,同步更新UI

适配器实现

先创建Item布局item_contact.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp"
    android:gravity="center_vertical">

    <CheckBox
        android:id="@+id/cb_select"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"/>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textStyle="bold"/>

        <TextView
            android:id="@+id/tv_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textColor="@android:color/darker_gray"
            android:layout_marginTop="4dp"/>
    </LinearLayout>
</LinearLayout>

然后编写适配器:

class ContactMultiSelectAdapter(
    private val contacts: List<Contact>
) : RecyclerView.Adapter<ContactMultiSelectAdapter.ContactViewHolder>() {

    // 存储选中的联系人ID
    private val selectedContactIds = mutableSetOf<String>()

    inner class ContactViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val cbSelect: CheckBox = itemView.findViewById(R.id.cb_select)
        val tvName: TextView = itemView.findViewById(R.id.tv_name)
        val tvPhone: TextView = itemView.findViewById(R.id.tv_phone)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_contact, parent, false)
        return ContactViewHolder(view)
    }

    override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
        val contact = contacts[position]
        holder.tvName.text = contact.name
        holder.tvPhone.text = contact.phoneNumber
        // 设置选中状态
        holder.cbSelect.isChecked = selectedContactIds.contains(contact.id)

        // Item点击事件
        holder.itemView.setOnClickListener {
            if (selectedContactIds.contains(contact.id)) {
                selectedContactIds.remove(contact.id)
            } else {
                selectedContactIds.add(contact.id)
            }
            notifyItemChanged(position)
        }

        // CheckBox与Item点击联动
        holder.cbSelect.setOnClickListener {
            holder.itemView.performClick()
        }
    }

    override fun getItemCount(): Int = contacts.size

    // 获取选中的联系人列表
    fun getSelectedContacts(): List<Contact> {
        return contacts.filter { selectedContactIds.contains(it.id) }
    }

    // 全选/取消全选(可选)
    fun toggleAllSelect() {
        if (selectedContactIds.size == contacts.size) {
            selectedContactIds.clear()
        } else {
            selectedContactIds.addAll(contacts.map { it.id })
        }
        notifyDataSetChanged()
    }
}

4. 初始化RecyclerView

在Activity/Fragment中绑定数据:

private fun setupRecyclerView(contacts: List<Contact>) {
    val recyclerView = findViewById<RecyclerView>(R.id.rv_contacts)
    recyclerView.layoutManager = LinearLayoutManager(this)
    val adapter = ContactMultiSelectAdapter(contacts)
    recyclerView.adapter = adapter

    // 示例:点击按钮获取选中联系人
    findViewById<Button>(R.id.btn_get_selected).setOnClickListener {
        val selected = adapter.getSelectedContacts()
        selected.forEach {
            Log.d("Selected", "${it.name} - ${it.phoneNumber}")
        }
    }
}

5. 可选优化

  • 长按Item进入批量选择模式(仿系统联系人)
  • 选中时修改Item背景色,增强视觉反馈
  • 过滤无手机号的联系人
  • 分页加载大量联系人避免卡顿

内容的提问来源于stack exchange,提问作者Optix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:15:31