android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

作者:游小陈 时间:2023-02-22 16:18:35 

前言:

仿微信通讯录搜索功能,通过汉字或拼音首字母找到匹配的联系人并显示匹配的位置

一:先看效果图

android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

字母索引

android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

搜索匹配

二:功能分析

1:汉字转拼音

通讯录汉字转拼音(首个字符当考虑姓氏多音字), 现在转换拼音常见的有pinyin4j和tinypinyin, pinyin4j的功能强大,包含声调多音字,tinypinyin执行快占用内存少, 如果只是简单匹配通讯录,建议使用tinypinyin,用法也很简单这里不详细介绍

拼音类


public class CNPinyin <T extends CN> implements Serializable, Comparable<CNPinyin<T>> {

/**
  * 对应首字首拼音字母
  */
 char firstChar;
 /**
  * 所有字符中的拼音首字母
  */
 String firstChars;
 /**
  * 对应的所有字母拼音
  */
 String[] pinyins;

/**
  * 拼音总长度
  */
 int pinyinsTotalLength;

public final T data;

CNPinyin(T data) {
   this.data = data;
 }

public char getFirstChar() {
   return firstChar;
 }

@Override
 public String toString() {
   StringBuilder sb = new StringBuilder().append("--firstChar--").append(firstChar).append("--pinyins:");
   for (String str : pinyins) {
     sb.append(str);
   }
   return sb.toString();
 }

int compareValue() {
   if (firstChar == DEF_CHAR) {
     return 'Z' + 1;
   }
   return firstChar;
 }

@Override
 public int compareTo(CNPinyin<T> tcnPinyin) {
   int compare = compareValue() - tcnPinyin.compareValue();
   if (compare == 0) {
     String chinese1 = data.chinese();
     String chinese2 = tcnPinyin.data.chinese();
     return chinese1.compareTo(chinese2);
   }
   return compare;
 }
}

2:定义索引栏 a~z,#控件

ItemDecoration配合RecyclerView实现StickyHeader效果,此效果很常见不详细介绍

3:根据转换好的拼音快速匹配

搜索匹配才是核心, 以下匹配原则,有优先顺序如果有匹配成功不执行后面的匹配原则

a:匹配原字符 并找出所匹配的起始位置与结束位置,如有中文匹配将不执行后面的拼音匹配原则


static CNPinyinIndex matcherChinese(CNPinyin cnPinyin, String keyword) {
   if (keyword.length() < cnPinyin.data.chinese().length()) {
     Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.data.chinese());
     if (matcher.find()) {
       return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end());
     }
   }
   return null;
}

b:匹配单个字符拼音的首个字母(例如"游小陈"可以匹配y, x, c, yx, xc, yxc)


static CNPinyinIndex matcherFirst(CNPinyin cnPinyin, String keyword) {
   if (keyword.length() <= cnPinyin.pinyins.length) {
     Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.firstChars);
     if (matcher.find()) {
       return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end());
     }
   }
   return null;
}

c:所有字符拼音的匹配, 且第一个匹配位置的拼音必须一致(例如"游小陈 youxiaochen", 必须匹配yo, you, xi, xia, xiao, ch, che, chen开头等 例如 yo youx, youxi, youxiao, xiaoc, xiaoch, xiaochen等等)


/**
  * 所有拼音匹配
  * @param cnPinyin
  * @param keyword
  * @return
  */
 static CNPinyinIndex matchersPinyins(CNPinyin cnPinyin, String keyword) {
   if (keyword.length() > cnPinyin.pinyinsTotalLength) return null;
   int start = -1;
   int end = -1;
   for (int i = 0; i < cnPinyin.pinyins.length; i++) {
     String pat = cnPinyin.pinyins[i];
     if (pat.length() >= keyword.length()) {//首个位置索引
       Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(pat);
       if (matcher.find() && matcher.start() == 0) {
         start = i;
         end = i + 1;
         break;
       }
     } else {
       Matcher matcher = Pattern.compile(pat, Pattern.CASE_INSENSITIVE).matcher(keyword);
       if (matcher.find() && matcher.start() == 0) {//全拼匹配第一个必须在0位置
         start = i;
         String left = matcher.replaceFirst("");
         end = end(cnPinyin.pinyins, left, ++i);
         break;
       }
     }
   }
   if (start >= 0 && end >= start) {
     return new CNPinyinIndex(cnPinyin, start, end);
   }
   return null;
 }

/**
  * 根据匹配字符递归查找下一结束位置
  * @param pinyinGroup
  * @param pattern
  * @param index
  * @return -1 匹配失败
  */
 private static int end(String[] pinyinGroup, String pattern, int index) {
   if (index < pinyinGroup.length) {
     String pinyin = pinyinGroup[index];
     if (pinyin.length() >= pattern.length()) {//首个位置索引
       Matcher matcher = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher(pinyin);
       if (matcher.find() && matcher.start() == 0) {
         return index + 1;
       }
     } else {
       Matcher matcher = Pattern.compile(pinyin, Pattern.CASE_INSENSITIVE).matcher(pattern);
       if (matcher.find() && matcher.start() == 0) {//全拼匹配第一个必须在0位置
         String left = matcher.replaceFirst("");
         return end(pinyinGroup, left, index + 1);
       }
     }
   }
   return -1;
 }

最后附上源码https://github.com/youxiaochen/ContactList

来源:http://www.jianshu.com/p/18a7bdb8905a

标签:android,通讯录,搜索
0
投稿

猜你喜欢

  • java web开发之购物车功能实现示例代码

    2023-01-24 16:52:55
  • SpringBoot整合OpenApi的实践

    2023-08-03 11:59:55
  • springboot使用mybatis一对多的关联查询问题记录

    2023-05-25 14:31:03
  • java弱口令检测机制解析

    2022-09-27 01:07:06
  • Android运用BroadcastReceiver实现强制下线

    2021-07-20 19:49:29
  • springboot mybatis里localdatetime序列化问题的解决

    2023-06-25 06:58:18
  • Android系统优化Ninja加快编译

    2022-06-30 19:43:14
  • C语言实现餐饮管理系统

    2023-03-26 06:40:37
  • Android使用GridView实现横向滚动效果

    2022-01-29 13:32:16
  • java虚拟机学习笔记进阶篇

    2022-01-07 04:53:06
  • Java客户端调用.NET的WebService实例

    2023-11-03 17:22:00
  • ReentrantLock获取锁释放锁的流程示例分析

    2021-08-05 20:51:10
  • Java线程公平锁和非公平锁的差异讲解

    2022-06-26 09:59:09
  • Java实例讲解Comparator的使用

    2021-07-15 09:16:58
  • 浅析Java中JSONObject和JSONArray使用

    2022-06-05 14:58:30
  • kotlin 协程上下文异常处理详解

    2022-09-30 06:32:52
  • Spring boot2X负载均衡和反向代理实现过程解析

    2023-02-06 04:18:53
  • C#如何Task执行任务,等待任务完成

    2022-03-06 11:31:31
  • C#基于Socket的TCP通信实现聊天室案例

    2021-12-19 15:54:13
  • Java如何使用ReentrantLock实现长轮询

    2023-11-26 00:09:08
  • asp之家 软件编程 m.aspxhome.com