【聲明:】本文是作者(蘑菇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包下面:
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掉: