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

3,860 阅读2分钟
原文链接: www.jianshu.com

前言:

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

更多文章请关注:www.jianshu.com/u/b1cff3409…


一:先看效果图
字母索引
字母索引
搜索匹配
搜索匹配
二:功能分析
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;
    }

更多文章请关注:www.jianshu.com/u/b1cff3409…

最后附上源码github.com/youxiaochen…