You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android通讯录去重问题:带区号与无区号号码未被识别为重复

解决Android通讯录RecyclerView中带区号与不带区号号码的去重问题

嗨!新年快乐呀~ 你的问题我完全get到了:同一个联系人的号码,带+91区号和不带区号的版本被当成了两个独立条目,导致HashSet去重失效对吧?咱们一步步来修复这个问题。

先分析现有代码的核心问题

  1. equals方法逻辑错误:你用==比较字符串(Java里字符串比较必须用equals()),而且条件写反了——应该是当两个联系人的号码和姓名匹配时返回true,而不是false;另外contains的判断逻辑也不合理,比如短号码可能被长号码包含,但其实不是同一个联系人。
  2. hashCode方法未标准化:你直接用原始号码的hashCode,带+91和不带的hash值肯定不一样,HashSet自然会认为是不同对象。
  3. 号码未统一格式:查询到的原始号码格式不统一,没有提前处理成标准化的格式,这是问题的根源。

解决方案:标准化号码+修正equals/hashCode

第一步:编写号码标准化工具方法

把所有号码转换成统一格式(比如去掉所有非数字字符,再移除开头的91前缀),这样+9195661911619566191161都会变成9566191161

private String normalizePhoneNumber(String rawPhoneNumber) {
    // 去掉所有非数字字符(包括空格、+、括号、短横线等)
    String cleanedNumber = rawPhoneNumber.replaceAll("[^0-9]", "");
    // 如果号码以91开头(对应+91的区号),移除前缀
    if (cleanedNumber.startsWith("91")) {
        cleanedNumber = cleanedNumber.substring(2);
    }
    return cleanedNumber;
}

第二步:修改ContactVO类,基于标准化号码实现equals和hashCode

在ContactVO中添加标准化号码的字段(推荐提前初始化,避免重复计算):

import java.util.Objects;

public class ContactVO {
    private String imageUri;
    private String contactName;
    private String contactNumber;
    private String normalizedContactNumber; // 新增标准化号码字段

    // 构造函数中初始化标准化号码
    public ContactVO(String imageUri, String contactName, String contactNumber) {
        this.imageUri = imageUri;
        this.contactName = contactName;
        this.contactNumber = contactNumber;
        this.normalizedContactNumber = normalizePhoneNumber(contactNumber);
    }

    // 内部实现号码标准化
    private String normalizePhoneNumber(String rawPhoneNumber) {
        String cleanedNumber = rawPhoneNumber.replaceAll("[^0-9]", "");
        if (cleanedNumber.startsWith("91")) {
            cleanedNumber = cleanedNumber.substring(2);
        }
        return cleanedNumber;
    }

    // 重写equals方法,基于标准化号码和姓名判断
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        ContactVO other = (ContactVO) obj;
        return Objects.equals(this.normalizedContactNumber, other.normalizedContactNumber) &&
                Objects.equals(this.contactName, other.contactName);
    }

    // 重写hashCode方法,基于标准化号码和姓名生成
    @Override
    public int hashCode() {
        return Objects.hash(normalizedContactNumber, contactName);
    }

    // 其他getter方法
    public String getImageUri() { return imageUri; }
    public String getContactName() { return contactName; }
    public String getContactNumber() { return contactNumber; }
}

第三步:修改联系人查询代码,直接使用标准化逻辑

在把号码存入ContactVO时,构造函数会自动处理标准化,无需额外操作:

Cursor phones = getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while (phones.moveToNext()) {
    String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
    String rawPhone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    String imageUri = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
    
    // 直接创建ContactVO,构造函数自动处理号码标准化
    contactVOList.add(new ContactVO(imageUri, name, rawPhone));
}

// 去重逻辑保持不变
Set<ContactVO> contactSet = new HashSet<>(contactVOList);
contactVOList = new ArrayList<>(contactSet);

// 后续Adapter初始化代码不变
mAdapter = new AllContactsAdapter(getContext(), contactVOList, userId, mobilenumber);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
rvContacts.setLayoutManager(mLayoutManager);
rvContacts.setItemAnimator(new DefaultItemAnimator());
rvContacts.setAdapter(mAdapter);

额外提示

如果你的应用需要支持其他国家区号,可以扩展标准化方法,比如根据用户所在地区自动识别并移除对应前缀,或者统一保留国际格式(比如所有号码都加上+91),只要保证同一个号码的不同格式最终转换成相同的字符串即可。

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

火山引擎 最新活动