android 車機電話的通訊錄聯繫人搜索實現解析 ------- 填坑日記

項目中的android 車機系統 搜索聯繫人算法一直有問題 , 這裏就把整個的流程寫一遍

 

一 . 搜索算法實現的功能

1.支持中文,英文搜索

2.支持電話號碼搜索

3.支持漢語拼音搜索, 首字母搜索也能支持, 同時對檢索到的文字顯示高亮

前面的第一, 第二點, 都是比較簡單的, 通過遍歷字符串, 查看字符串是否包含用戶輸入的字符, 就能達到檢索的功能

重點是第三點,拼音的搜索,首字母搜索

 

二. 拼音搜索的流程

1.先介紹幾個類

ContactData.java , 裏面保存有手機端同步過來的數據
public class ContactData implements Serializable, Parcelable {
    public static final int CONTACT_NAME_MAX_LENGTH = 16;
    public static final int CONTACT_PHONE_MAX_LENGTH = 32;
    private String name = "";
    private String letter = "";

    private List<Integer> phoneNumTypeList = new ArrayList<>();

    private List<String> phoneNumList = new ArrayList<>();

    public List<NumberData> numbers = new ArrayList<>();

    private List<String> addressList = new ArrayList<>();

    public int photoType;
    public byte[] photoBytes;

     ....  //一些可能用到的方法

}
SearchResult.java  , 搜索結果實體類 . 
public class SearchResult implements Serializable, Cloneable, Parcelable {
    private static final long serialVersionUID = -8624630249543035384L;

    public long contactID = -1;

    public String name = "";

    public String phone;
    public String time;
    public int count = 1;
    // 加入優先級 排序用的
    int priority = -1;
    public String pinyin;

    public String number;
    public String initials;
    public String pinyinInitials;

    // 高亮
    public String[] highlight;
    // 分解
    ArrayList<PyNode> nodes;

    public int photoType;
    public byte[] photoBytes;

    ...// 一些可能使用到的方法
}

Pinyin.java 這個類來自於開源的項目 com.github.promeg.pinyinhelper , 通過下面的語句,可以將中文轉化爲拼音, 具體的原理, 不詳,有時間再看看

            // 漢字
            String pinyin = Pinyin.toPinyin(c);

 

 

2.實現流程, 

a.先將同步到的聯繫人數據contacts 轉爲searchResult 數據,  這個過程就是將名字數據轉換爲拼音,  生成首字母數據pinyinInitials 

    private List<SearchResult> convertFormatSearchResult(List<ContactData> contactsList) {
        Lg.i(TAG, " convertFormatSearchResult ");
        if (contactsList == null || contactsList.isEmpty()) {
            return Lists.newArrayList();
        }

        List<SearchResult> searchResults = new ArrayList<>();

        String phoneNumber;
        String name;

        String tempKey = null;
        for (ContactData phones : contactsList) {

            int size = phones.numbers.size();
            for (int i = 0; i < size; i++) {
                name = phones.getName();
                phoneNumber = phones.numbers.get(i).number;

                SearchResult phoneContact = new SearchResult();

                phoneContact.name = name;
                phoneContact.phone = phoneNumber;

                // 分字
                phoneContact.formatPinYin();

                String pinyinInitials = PinyinUtils.cn2FirstSpell(name, false);
                String pinyin = PinyinUtils.cn2Spell(name, false, true, true, "");

                String initialsNumber = PinyinUtils.pinyinConvertToNumber(pinyinInitials);
                String pinyinNumber = PinyinUtils.pinyinConvertToNumber(pinyin);

                // 取得名字所有字母轉化爲拼音
                phoneContact.pinyin = pinyin;
                // 取首字母 並 轉換爲 數字
                phoneContact.initials = initialsNumber;
                phoneContact.pinyinInitials = pinyinInitials;
                // 取所有字母 並 轉化爲 數字
                phoneContact.number = pinyinNumber;

                phoneContact.photoType = phones.photoType;
                phoneContact.photoBytes = phones.photoBytes;

                searchResults.add(phoneContact);
            }
        }
        return searchResults;
    }

b. 建立循環, 遍歷所有的已知searchResult ,    查看與檢索項匹配的聯繫人, 添加到 result 中

    public static List<SearchResult> searchContactByAll(final List<SearchResult> source, final String query) {
        List<SearchResult> result = new ArrayList<>();
        // 漢語轉拼音
        String pyInput = PinyinUtils.cn2Spell(query, false, true, true, "");
        String pyInitialInput = PinyinUtils.cn2FirstSpell(query, false);
        for (SearchResult user : source) {
            // 重置高亮
            user.resetAllHighlight();
            // 標識是否包含搜索內容
            boolean flag = false;
            // 搜索內容 不能大於 聯繫人拼音的長度
            // 搜索中文的時候, 我們直接遍歷名字
            if (pyInput.length() <= user.pinyin.length()) {
                if(StringUtils.checkChinese(query)){
                    if (user.name.contains(query)) {
                        user.priority = 1;
                        if(user.name.startsWith(query)) {
                            // 優先級爲最大
                            user.priority = 0;
                        }
                        //由於名字中可能含有多音字,所以使用name.indexof
                        int j = user.name.indexOf(query);
                        //避免這裏出現數組越界
                        for (int i = 0; i < user.nodes.size() - j; i++) {
                            // 設置高亮
                            updateHighlight(user.nodes.get(j + i), -1);
                        }
                        result.add(user);
                    }
                    continue;
                }

                if (StringUtils.checkChinese(user.name)) {
                    if (user.name.contains(query)) {
                        user.priority = 1;
                        if(user.name.startsWith(query)) {
                            // 優先級爲最大
                            user.priority = 0;
                        }

                        //由於名字中可能含有多音字,所以使用name.indexof
                        int j = user.name.indexOf(query);
                        //避免這裏出現數組越界
                        for (int i = 0; i < user.nodes.size() - j; i++) {
                            // 設置高亮
                            updateHighlight(user.nodes.get(j + i), -1);
                        }
                        flag = true;
                    } else if (user.pinyin.toLowerCase().startsWith(pyInput.toLowerCase())) {// 直接拼音匹配
                        // 優先級爲1
                        user.priority = 2;
                        //避免這裏出現數組越界
                        for (int i = 0; i < user.nodes.size(); i++) {
                            updateHighlight(user.nodes.get(i), -1);
                        }
                        flag = true;
                    } else {
                        // 深層拷貝ArrayList
                        ArrayList<PyNode> nodes = Lists.newArrayList();
                        for (PyNode pyNode : user.nodes) {
                            try {
                                PyNode node = (PyNode) pyNode.clone();
                                node.number = node.pinyin; // 注意這裏換掉T9算法中的number,使用拼音代替!
                                nodes.add(node);
                            } catch (CloneNotSupportedException e) {
                                e.printStackTrace();
                            }
                        }
                        // 全部轉換爲小寫
                        pyInput = pyInput.toLowerCase(Locale.getDefault());
                        // 開啓遞歸搜索
                        flag = pinyinRecursion(pyInput, 0, 1, 0, nodes, false);
                        // 優先級
                        user.priority = user.getNodeHighlightString().indexOf("1") + 400;
                    }
                } else {
                    // 英文
                    String name = user.pinyin.toLowerCase(Locale.getDefault());
                    String key = query.toLowerCase(Locale.getDefault());
                    if (name.contains(key)) {
                        int length = key.length(), j = name.indexOf(key);

                        // 優先級
                        user.priority = j + 1200;

                        if(name.startsWith(key)) {
                            user.priority = j + 800;
                        }

                        // 設置高亮
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < length; i++) {
                            sb.append(1);
                        }
                        StringBuilder highlight = new StringBuilder(user.highlight[0]);
                        highlight.replace(j, length + j, sb.toString());
                        user.highlight[0] = highlight.toString();
                        flag = true;
                    }
                }
            }
            // 搜索電話
            if (!flag && user.phone.contains(query)) {

                if(user.phone.equals(query)) {
                    user.priority = 9997;
                } else if(user.phone.startsWith(query)) {
                    user.priority = 9998;
                } else {
                    user.priority = 9999 + user.phone.indexOf(query);
                }

                // 重置高亮
                user.resetAllHighlight();
                flag = true;
            }
            // 判斷是否匹配成功
            if (flag) {
                result.add(user);
            }
        }
        Collections.sort(result, new ListComparator());
        return result;
    }

 

3.總結 :

主要工作就是兩點, 一是把名字轉化爲拼音, 二是找到匹配的聯繫人項, 並標註匹配的字串

 

之前一直有個問題,

1. 不支持中文全拼檢索 , 發現是已經檢索到,只是數組越界了, 

 2. 中文會被先轉爲拼音,然後檢索.   這個也是不對的,  正確的做法是中文檢索優先級最高

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章