实操:MVVM实现Android通讯录功能

目录

1 权限:访问通讯录

2 实现:读取、使用通讯录

2.1 View层:Activity中懒加载ViewModel

2.2 ViewModel层:定义访问Model层方法以及LiveData

2.3 Model层:定义数据类Bean以及Model实现

2.3.1 数据类Bean:联系人信息

2.3.2 Model获取手机所有联系人数据

3 其他业务功能

3.1 跳转到系统短信页面

3.2 获取联系人头像


1 权限:访问通讯录

        首先,在Android6.0及以上,需在manifest声明读取通讯录的权限,并在代码中动态监测和申请权限。

android.Manifest.permission 

public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"

2 实现:读取、使用通讯录

2.1 View层:Activity中懒加载ViewModel

    private val viewModel by lazy {
        ViewModelProvider(this).get(InviteFriendsViewModel::class.java)
    }

    private fun initViewModel() {
        viewModel.readContacts(contentResolver)
        
        viewModel.getContactsListLiveData.observe(this) { contactsList ->
            clearLoadingAnimation()

            if (contactsList.isEmpty()) {
                showEmptyView()
                return@observe
            }
            adapter.setNewData(contactsList)
        }
    }

2.2 ViewModel层:定义访问Model层方法以及LiveData

class InviteFriendsViewModel : ViewModel() {

    val getContactsListLiveData by lazy {
        MutableLiveData<MutableList<ContactInfoBean>>()
    }

    fun readContacts(resolver: ContentResolver) {
        viewModelScope.launch {
            getContactsListLiveData.value = ContactsUtils.getContacts(resolver)
        }
    }

}

2.3 Model层:定义数据类Bean以及Model实现

2.3.1 数据类Bean:联系人信息

    data class ContactInfoBean(
        var id: String = "",
        var sortKey: String = "",
        var name: String,
        var phone: String
    )

        联系人信息字段,都是从Android联系人数据库中拿到,字段注解如下:

  1. id:作为联系人唯一标识,可在识别、更新联系人或获取联系人头像时使用;
  2. sortKey:读取的联系人按照姓名从 A->Z 排序分组,可在联系人排序时使用;
  3. name:联系人姓名,可在显示联系人时使用;
  4. number:联系人手机号,可显示手机号或跳转短信页面等。

2.3.2 Model获取手机所有联系人数据

object ContactsUtils {

    /**
     * 已自测验证:会拿到手机所有联系人数据。包括:手机联系人、SIM卡联系人(多卡联系人也能拿到数据)
     * 另外:联系人返回数据顺序根据手机厂商不同而有些许差异。如:
     *      华为P9:1、不能识别的字母;2、emoji;3、数字大小;4、A-Z字母数字。
     *      三星Note10:1、不能识别的字母;2、emoji;3、A-Z字母数字;4、数字大小。
     */
    private val KEY_PHONE_CONTACTS_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI

    suspend fun getContacts(resolver: ContentResolver): MutableList<ContactInfoBean> =
        withContext(Dispatchers.IO) { getPhoneContacts(resolver, KEY_PHONE_CONTACTS_URI) }

    /**
     * 获取手机所有联系人数据。包括:手机联系人、SIM卡联系人(多卡联系人也能拿到数据)
     */
    @SuppressLint("Range")
    suspend fun getPhoneContacts(
        resolver: ContentResolver, uri: Uri
    ): MutableList<ContactInfoBean> =
        withContext(Dispatchers.IO) {
            val contacts: MutableList<ContactInfoBean> = mutableListOf()
            var cursor: Cursor? = null
            try {
                // 在这里我们给query传递进去一个SORT_KEY_PRIMARY。 告诉ContentResolver获得的结果安装联系人名字的首字母有序排列。
                val projection = arrayOf("contact_id", "sort_key", "display_name", "data1")
                cursor = resolver.query(uri, projection, null, null, "sort_key")
                if (cursor != null) {
                    while (cursor.moveToNext()) {
                        // 联系人ID
                        val id = cursor.getString(cursor.getColumnIndex("contact_id"))
                        // Sort Key,读取的联系人按照姓名从 A->Z 排序分组。
                        val sortKey =
                            getSortKey(cursor.getString(cursor.getColumnIndex("sort_key")))
                        // 获取联系人姓名
                        val name: String = cursor.getString(cursor.getColumnIndex("display_name"))
                        // 获取联系人手机号
                        val number: String = cursor.getString(cursor.getColumnIndex("data1"))

                        if (name.isNotEmpty() && number.isNotEmpty()) {
                            contacts.add(ContactInfoBean(id, sortKey, name, number))
                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                cursor?.close()
            }
            contacts
        }

    /**
     * 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
     */
    private fun getSortKey(sortKeyString: String): String {
        val key = sortKeyString.substring(0, 1).uppercase(Locale.getDefault())
        return if (key.matches(Regex("[A-Z]"))) {
            key
        } else {
            "#"
        }
    }

}

3 其他业务功能

3.1 跳转到系统短信页面

    fun gotoSystemSMSPage(context: Context, content: String = "", number: String = "") {
        Intent.ACTION_SENDTO
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("smsto:"))
        // 设置发送的内容
        intent.putExtra("sms_body", content)
        // 没有电话号码的话为默认的,即显示的时候是为空的
        intent.putExtra("address", number)
        intent.type = "vnd.android-dir/mms-sms"
        context.startActivity(intent)
    }

3.2 获取联系人头像

    /**
     * 获取联系人头像
     */
    fun getContactsIcon(resolver: ContentResolver, contactsId: Int): Bitmap? {
        return BitmapFactory.decodeStream(
            ContactsContract.Contacts.openContactPhotoInputStream(
                resolver, Uri.withAppendedPath(
                    ContactsContract.Contacts.CONTENT_URI, contactsId.toString() + ""
                )
            )
        )
    }

猜你喜欢

转载自blog.csdn.net/Agg_bin/article/details/128903020
今日推荐