Android通訊錄中的模糊搜索(簡(jiǎn)拼或全拼)和多音字問(wèn)題

【聲明:】本文是作者(蘑菇v5)原創(chuàng),版權(quán)歸作者 蘑菇v5所有匀奏,侵權(quán)必究鞭衩。本文首發(fā)在簡(jiǎn)書。如若轉(zhuǎn)發(fā)娃善,請(qǐng)注明作者和來(lái)源地址论衍!未經(jīng)授權(quán),嚴(yán)禁私自轉(zhuǎn)載聚磺!


前段時(shí)間坯台,公司項(xiàng)目中需要加入聯(lián)系人功能,而聯(lián)系人數(shù)據(jù)保存在公司服務(wù)器上瘫寝,所以手機(jī)端通過(guò)OkHttp框架以https協(xié)議從服務(wù)器上獲取json格式的人員數(shù)據(jù)蜒蕾。

在開發(fā)的過(guò)程當(dāng)中稠炬,遇到了一些問(wèn)題,比如搜索的時(shí)候咪啡,要能夠以拼音全寫和簡(jiǎn)寫的形式首启,模糊搜索到相應(yīng)的人員,還有姓氏多音字的問(wèn)題撤摸,之前用的拼音轉(zhuǎn)換工具pinyin4j.jar毅桃,pinyin4j是一個(gè)流行的Java庫(kù),支持中文字符和拼音之間的轉(zhuǎn)換准夷,但是對(duì)于多音字沒(méi)有相應(yīng)的處理钥飞。而在Android的系統(tǒng)應(yīng)用聯(lián)系人中也給我們實(shí)現(xiàn)了漢字與拼接轉(zhuǎn)換的方式,那就是android提供的HanziToPinyin工具類衫嵌,成功處理了比如“單”姓所遇到的dan與shan代承。HanziToPinyin類代碼如下:

importandroid.text.TextUtils;
importandroid.util.Log;
importjava.util.ArrayList;
importlibcore.icu.Transliterator;
/**
*An object to convert Chinese character to its corresponding pinyin string.
*For characters with multiple possible pinyin string, only one is selected
*according to ICU Transliterator class. Polyphone is not supported in this
*implementation.
*/
public class HanziToPinyin {
    private static final String TAG = "HanziToPinyin";
    private static HanziToPinyin sInstance;
    private Transliterator mPinyinTransliterator;
    private Transliterator mAsciiTransliterator;

    public static class Token {
        /**
         * Separator between target string for each source char
         */
        public static final String SEPARATOR = " ";
        public static final int LATIN = 1;
        public static final int PINYIN = 2;
        public static final int UNKNOWN = 3;

        public Token() {
        }
        public Token(int type, String source, String target) {
            this.type = type;
            this.source = source;
            this.target = target;
        }

        /**
         * Type of this token, ASCII, PINYIN or UNKNOWN.
         */
        public int type;
        /**
         * Original string before translation.
         */
        public String source;
        /**
         * Translated string of source. For Han, target is corresponding Pinyin. Otherwise target is
         * original string in source.
         */
        public String target;
    }

    private HanziToPinyin() {
        try {
            mPinyinTransliterator = new Transliterator(
                    "Han-Latin/Names; Latin-Ascii; Any-Upper");
            mAsciiTransliterator = new Transliterator("Latin-Ascii");
        } catch (IllegalArgumentException e) {
            Log.w(TAG, "Han-Latin/Names transliterator data is missing,"
                    + " HanziToPinyin is disabled");
        }
    }

    public boolean hasChineseTransliterator() {
        return mPinyinTransliterator != null;
    }

    public static HanziToPinyin getInstance() {
        synchronized (HanziToPinyin.class) {
            if (sInstance == null) {
                sInstance = new HanziToPinyin();
            }
            return sInstance;
        }
    }

    private void tokenize(char character, Token token) {
        token.source = Character.toString(character);

        // ASCII
        if (character < 128) {
            token.type = Token.LATIN;
            token.target = token.source;
            return;
        }

        // Extended Latin. Transcode these to ASCII equivalents
        if (character < 0x250 || (0x1e00 <= character && character < 0x1eff)) {
            token.type = Token.LATIN;
            token.target = mAsciiTransliterator == null ? token.source :
                    mAsciiTransliterator.transliterate(token.source);
            return;
        }

        token.type = Token.PINYIN;
        token.target = mPinyinTransliterator.transliterate(token.source);
        if (TextUtils.isEmpty(token.target) ||
                TextUtils.equals(token.source, token.target)) {
            token.type = Token.UNKNOWN;
            token.target = token.source;
        }
    }

    public String transliterate(final String input) {
        if (!hasChineseTransliterator() || TextUtils.isEmpty(input)) {
            return null;
        }
        return mPinyinTransliterator.transliterate(input);
    }

    /**
     * Convert the input to a array of tokens. The sequence of ASCII or Unknown characters without
     * space will be put into a Token, One Hanzi character which has pinyin will be treated as a
     * Token. If there is no Chinese transliterator, the empty token array is returned.
     */
    public ArrayList<Token> getTokens(final String input) {
        ArrayList<Token> tokens = new ArrayList<Token>();
        if (!hasChineseTransliterator() || TextUtils.isEmpty(input)) {
            // return empty tokens.
            return tokens;
        }

        final int inputLength = input.length();
        final StringBuilder sb = new StringBuilder();
        int tokenType = Token.LATIN;
        Token token = new Token();

        // Go through the input, create a new token when
        // a. Token type changed
        // b. Get the Pinyin of current charater.
        // c. current character is space.
        for (int i = 0; i < inputLength; i++) {
            final char character = input.charAt(i);
            if (Character.isSpaceChar(character)) {
                if (sb.length() > 0) {
                    addToken(sb, tokens, tokenType);
                }
            } else {
                tokenize(character, token);
                if (token.type == Token.PINYIN) {
                    if (sb.length() > 0) {
                        addToken(sb, tokens, tokenType);
                    }
                    tokens.add(token);
                    token = new Token();
                } else {
                    if (tokenType != token.type && sb.length() > 0) {
                        addToken(sb, tokens, tokenType);
                    }
                    sb.append(token.target);
                }
                tokenType = token.type;
            }
        }
        if (sb.length() > 0) {
            addToken(sb, tokens, tokenType);
        }
        return tokens;
    }

    private void addToken(
            final StringBuilder sb, final ArrayList<Token> tokens, final int tokenType) {
        String str = sb.toString();
        tokens.add(new Token(tokenType, str, str));
        sb.setLength(0);
    }
    /**
     *  輸入漢字返回拼音的通用方法函數(shù)
     */
    public static String getPinYin(String hanzi) {
        ArrayList<Token> tokens = HanziToPinyin.getInstance().getTokens(hanzi);
        StringBuilder sb = new StringBuilder();
        if (tokens != null && tokens.size() > 0) {
            for (Token token : tokens) {
                if (Token.PINYIN == token.type) {
                    sb.append(token.target);
                } else {
                    sb.append(token.source);
                }
            }
        }

        return sb.toString().toUpperCase();
    }
}

上面的Transliterator類,一定要放在libcore.icu包下面:

圖1

Transliterator類的代碼如下:

public final class Transliterator {
    private long peer;
    /**
     * Creates a new Transliterator for the given id.
     */
    public Transliterator(String id) {
        peer = create(id);
    }
    @Override protected synchronized void finalize() throws Throwable {
        try {
            destroy(peer);
            peer = 0;
        } finally {
            super.finalize();
        }
    }
    /**
     * Returns the ids of all known transliterators.
     */
    public static native String[] getAvailableIDs();
    /**
     * Transliterates the specified string.
     */
    public String transliterate(String s) {
        return transliterate(peer, s);
    }
    private static native long create(String id);
    private static native void destroy(long peer);
    private static native String transliterate(long peer, String s);
}

在項(xiàng)目中使用的時(shí)候渐扮,以如下方式調(diào)用:

String pinyin=HanziToPinyin.getPinYin(name);

以上是通訊錄多音字的時(shí)候论悴,下面介紹如何模糊搜索,網(wǎng)上關(guān)于此方面的文章很多墓律,大都是拼音全拼和文字檢索膀估,本文介紹了拼音簡(jiǎn)寫的搜索。
首先耻讽,寫一個(gè)實(shí)體類察纯,如SortModel,用于方便存取對(duì)象中某個(gè)字段的數(shù)據(jù)针肥,如下:

public class SortModel extends Contact implements Serializable{
public SortModel() {
super();
}
public SortModel(String id,String usercode,String name,String pinyin,String status,String serverTime) {
super(id,usercode,name,pinyin,status,serverTime);
}
public SortModel(String id,String usercode,String name,String pinyin,String status) {
super(id,usercode,name,pinyin,status);
}
public String sortLetters;//顯示數(shù)據(jù)拼音的首字母
public SortToken sortToken= new SortToken();//中文全名,全拼,簡(jiǎn)拼
}

如下contact類:

public class Contact implements Serializable{
    public String id;
    public String usercode;
    public String name;
    public String pinyin;
    //人員狀態(tài)
    public String status;//add del update
    public boolean isChecked;
    //服務(wù)器時(shí)間
    public String serverTime;
    public Contact(){}
    public Contact(String id,String usercode,String name,String pinyin){
        this.id=id;
        this.usercode=usercode;
        this.name=name;
        this.pinyin=pinyin;
    }

    public Contact(String id, String usercode, String name, String pinyin, String status) {
        this.id = id;
        this.usercode = usercode;
        this.name = name;
        this.pinyin = pinyin;
        this.status = status;
    }

    public Contact(String id, String usercode, String name, String pinyin, String status, String serverTime) {
        this.id = id;
        this.usercode = usercode;
        this.name = name;
        this.pinyin = pinyin;
        this.status = status;
        this.serverTime = serverTime;
    }
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsercode() {
        return usercode;
    }

    public void setUsercode(String usercode) {
        this.usercode = usercode;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }
    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void setIsChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public boolean isChecked() {
        return isChecked;
    }
    public String getServerTime() {
        return serverTime;
    }

    public void setServerTime(String serverTime) {
        this.serverTime = serverTime;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "id='" + id + '\'' +
                ", usercode='" + usercode + '\'' +
                ", name='" + name + '\'' +
                ", pinyin='" + pinyin + '\'' +
                ", status='" + status + '\'' +
                ", isChecked=" + isChecked +
                '}';
    }
}

SortToken類:

/**
*拼音
*/
public class SortToken implements Serializable{
public String simpleSpell="";//簡(jiǎn)拼
public String wholeSpell="";//全拼
public String chName="";//中文全名
}

拼音簡(jiǎn)拼和全拼饼记,自己處理字符串的操作,這里不做詳細(xì)說(shuō)明了(作者是通過(guò)處理sortKey的方式慰枕,sortKey的格式:SHI 世 JIE 界 NI 你 HAO 好具则,自己寫了個(gè)工具類PinyinUtils,處理拼音:

 public class PinyinUtils {
    /**
     * 名字轉(zhuǎn)拼音,取首字母
     * @param name
     * @return
     */
    public static String getSortLetter(String name,String pinyin) {
        String letter = "#";
        if (name == null) {
            return letter;
        }
        String sortString = pinyin.substring(0, 1).toUpperCase(Locale.CHINESE);

        // 正則表達(dá)式具帮,判斷首字母是否是英文字母
        if (sortString.matches("[A-Z]")) {
            letter = sortString.toUpperCase(Locale.CHINESE);
        }
        return letter;
    }

    private static final String chReg = "[\\u4E00-\\u9FA5]+";//中文字符串匹配
    //String chReg="[^\\u4E00-\\u9FA5]";//除中文外的字符匹配
    /**
     * 解析sort_key,封裝簡(jiǎn)拼,全拼
     * @param sortKey
     * @return
     */
    public static SortToken parseSortKey(String sortKey) {
        SortToken token = new SortToken();
        if (sortKey != null && sortKey.length() > 0) {
            //其中包含的中文字符
            String[] enStrs = sortKey.replace(" ", "").split(chReg);
            for (int i = 0, length = enStrs.length; i < length; i++) {
                if (enStrs[i].length() > 0) {
                    //拼接簡(jiǎn)拼
                    token.simpleSpell += enStrs[i].charAt(0);
                    //拼接全拼
                    token.wholeSpell += enStrs[i];
                }
            }
        }
        return token;
    }
}

下面說(shuō)一下博肋,模糊查詢的方法,如下:

/**
*通過(guò)名字或者拼音搜索
* @paramstr
* @return
*/
  public List<SortModel> searchContact(final String str, List<SortModel> mAllContactsList){
        List<SortModel> filterList = new ArrayList<SortModel>();// 過(guò)濾后的list
        //if (str.matches("^([0-9]|[/+])*$")) {// 正則表達(dá)式 匹配號(hào)碼
        if (str.matches("^([0-9]|[/+]).*")) {// 正則表達(dá)式 匹配以數(shù)字或者加號(hào)開頭的字符串(包括了帶空格及-分割的號(hào)碼)
            for (SortModel contact : mAllContactsList) {
                if (contact.name != null) {
                    if (contact.name.contains(str)) {
                        if (!filterList.contains(contact)) {
                            filterList.add(contact);
                        }
                    }
                }
            }
        }else {
            for (SortModel contact : mAllContactsList) {
                if (contact.name != null) {
                    //姓名全匹配,姓名首字母簡(jiǎn)拼匹配,姓名全字母匹配
                    if (contact.name.toLowerCase(Locale.CHINESE).contains(str.toLowerCase(Locale.CHINESE))
                            || contact.sortToken.simpleSpell.toLowerCase(Locale.CHINESE).contains(str.toLowerCase(Locale.CHINESE))
                            || contact.sortToken.wholeSpell.toLowerCase(Locale.CHINESE).contains(str.toLowerCase(Locale.CHINESE))) {
                        if (!filterList.contains(contact)) {
                            filterList.add(contact);
                        }
                    }
                }
            }
        }
        return filterList;
    }

通過(guò)以上方式能夠處理自己在項(xiàng)目中遇到的問(wèn)題蜂厅,截個(gè)效果圖吧匪凡,運(yùn)行在自己手機(jī)上的效果,圖標(biāo)和標(biāo)題已ps掉:

圖2
圖3
圖4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掘猿,一起剝皮案震驚了整個(gè)濱河市病游,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稠通,老刑警劉巖衬衬,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轻绞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡佣耐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門唧龄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兼砖,“玉大人,你說(shuō)我怎么就攤上這事既棺》硇” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵丸冕,是天一觀的道長(zhǎng)耽梅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)胖烛,這世上最難降的妖魔是什么眼姐? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮佩番,結(jié)果婚禮上众旗,老公的妹妹穿的比我還像新娘。我一直安慰自己趟畏,他們只是感情好贡歧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赋秀,像睡著了一般利朵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猎莲,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天绍弟,我揣著相機(jī)與錄音,去河邊找鬼著洼。 笑死晌柬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的郭脂。 我是一名探鬼主播年碘,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼展鸡!你這毒婦竟也來(lái)了屿衅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莹弊,失蹤者是張志新(化名)和其女友劉穎涤久,沒(méi)想到半個(gè)月后涡尘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡响迂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年考抄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔗彤。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡川梅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出然遏,到底是詐尸還是另有隱情贫途,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布待侵,位于F島的核電站丢早,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秧倾。R本人自食惡果不足惜怨酝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望那先。 院中可真熱鬧凫碌,春花似錦、人聲如沸胃榕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)勋又。三九已至苦掘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間楔壤,已是汗流浹背鹤啡。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹲嚣,地道東北人递瑰。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隙畜,于是被迫代替她去往敵國(guó)和親抖部。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容