這個是我做Android以來碰到的最煩的東西,該死的emoji表情,恨之入骨。。無奈這個問題分配給我了。我也只能硬著頭皮做。
0.吐個槽先
首先,你要明白什麼是emoji表情,不知道的google,不需要支援emoji的可以繞道了。
emoji有很多不同的版本,我tm最討厭的就是不同版本的了。Unified DoCoMo KDDI Softbank Google
因為ios5升級了,emoji編碼從softbank變成unified了。所以只能Android這邊改了。傷心。
我要做的工作就是把訊息中含有的unified的emoji編碼過濾出來,然後映射出對應的表情資源顯示出來。
1.準備工作。
google下emoji表情的曆史,google code上有個源碼,不過是java的,那麼你要把它改成java的。。(記得當初解析gif表情時也是把java改成android,可憐的我為咩總是幹這活。。)
這裡有個link,上面有所有的編碼對應轉換。Emoji for PHP
然後google code裡有所有對應的編碼轉換的xml。叫emoji4unicode.xml. 這裡是link: emoji4unicode
如果沒興趣我等下會直接貼代碼的,但是最好你先自己弄明白解析的原理是什麼,emoji表情一直在增加,以後要相容你就得自己想辦法了。
2.舉個例子
black sun with rays unified : U+2600 softbank: U+E04A
如果以前你解析過softbank,應該很熟悉了。好歹它還有個大致得順序,解析得時候只要判斷是否在這個unicode範圍內就可以了。so easy
可是unified完全是無順序得,所以必須得自己建好映射。
因為原始的emoji.xml很大,把所有的描述資訊都放裡面了,我不需要,所以我寫了個java把xml解析了一遍,然後重建了一個我需要的xml。貼個圖出來大家看下。
you see , 這個原始的xml實在太大了,有162K,而且還是xml解析。。你想想多耗記憶體和時間。所以必須把它再轉換一遍。
so 這個過程做好了,就可以進行下一步真正的解析了。。
3 解析過程。
因為emoji是有表情分組的,所以你要優先考慮解析出來的也是分好組的。
HashMap<String, ArrayList<String>> emoMap = new HashMap<String, ArrayList<String>>();
我是這麼來分組的。
解析xml,然後把2600這樣的字串轉換成unicode。這個很關鍵,映射對應不上肯定也解析不出來。要注意的一點是有的emoji是兩個unicode組成的 U+1F1F0 U+1F1F7 這樣。。所以麼。又多了一個環節。
HashMap<List<Integer>, String> convertMap = new HashMap<List<Integer>, String>();
再定義一個map來存unicode和string字元的映射。
if (xmlpull.getName().equals("e")) {
fromAttr = xmlpull.nextText();
emos.add(fromAttr);
List<Integer> fromCodePoints = new ArrayList<Integer>();
if (fromAttr.length() > 6) {
String[] froms = fromAttr.split("\\_");
for (String part : froms) {
fromCodePoints.add(Integer.parseInt(part, 16));
}
} else {
fromCodePoints.add(Integer.parseInt(fromAttr, 16));
}
convertMap.put(fromCodePoints, fromAttr);
}
這樣就把整個解析都寫到記憶體裡了。這個就做成單例咯,在程式一進來,application裡初始化。
4,解析過程
這裡我是把emoji表情解析出來,再改成[e]2600[/e]的形式。然後再用正則再解析一遍(為什麼這樣做。。因為訊息裡面不可能只有emoji表情的啊親。。。你還要解析另外的表情。)
下面是過濾emoji表情的方法:
public String parseEmoji(String input) {
if (input == null || input.length() <= 0) {
return "";
}
StringBuilder result = new StringBuilder();
int[] codePoints = toCodePointArray(input);
List<Integer> key = null;
for (int i = 0; i < codePoints.length; i++) {
key = new ArrayList<Integer>()
if (i + 1 < codePoints.length) {
key.add(codePoints[i]);
key.add(codePoints[i + 1]);
if (convertMap.containsKey(key)) {
String value = convertMap.get(key);
if (value != null) {
result.append("[e]" + value + "[/e]");
}
i++;
continue;
}
}
key.clear();
key.add(codePoints[i]);
if (convertMap.containsKey(key)) {
String value = convertMap.get(key);
if (value != null) {
result.append("[e]" + value + "[/e]");
}
continue;
}
result.append(Character.toChars(codePoints[i]));
}
return result.toString();
}
看到這裡,你可能覺得懵了。當時我也是卡了兩三天,不停的網上找資料,反編譯別人的代碼。最後才找的解決方案的,代碼其實沒有很多,最重要的是要怎麼去思考。
4.渲染成圖片。
public static SpannableStringBuilder convetToHtml(String content, Context mContext) {
String regex = "\\[e\\](.*?)\\[/e\\]";
Pattern pattern = Pattern.compile(regex);
String emo = "";
Resources resources = mContext.getResources();
String unicode = EmojiParser.getInstance(mContext).parseEmoji(content);
Matcher matcher = pattern.matcher(unicode);
SpannableStringBuilder sBuilder = new SpannableStringBuilder(unicode);
Drawable drawable = null;
ImageSpan span = null;
while (matcher.find()) {
emo = matcher.group();
try {
int id = resources.getIdentifier(
"emoji_" + emo.substring(emo.indexOf("]") + 1, emo.lastIndexOf("[")),
"drawable", "package");
if (id != 0) {
drawable = resources.getDrawable(id);
drawable.setBounds(0, 0, 24, 24);
span = new ImageSpan(drawable);
sBuilder.setSpan(span, matcher.start(), matcher.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
break;
}
}
return sBuilder;
}
好了,成功解析出來了。但是!!你得先把圖片弄到手啊,親。而且還要我這個格式得。
貼個圖
額,話說這個我也是寫了個java,把之前得softbank得命名轉換成unified的命名,當初我還準備一個一個手動改來著,還好我同事一語點醒夢中人額。。不然我估計就吐血身亡了。程式寫多了就是這樣,思維固定了。。。
5.發emoji訊息。
既然能顯示了,你還得發啊,親。
直接貼代碼把,一個還原過程
public static String convertToMsg(CharSequence cs, Context mContext) {
SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
ImageSpan[] spans = ssb.getSpans(0, cs.length(), ImageSpan.class);
for (int i = 0; i < spans.length; i++) {
ImageSpan span = spans[i];
String c = span.getSource();
int a = ssb.getSpanStart(span);
int b = ssb.getSpanEnd(span);
if (c.contains("emoji")) {
ssb.replace(a, b, convertUnicode(c));
}
}
ssb.clearSpans();
return ssb.toString();
}
private static String convertUnicode(String emo) {
emo = emo.substring(emo.indexOf("_") + 1);
if (emo.length() < 6) {
return new String(Character.toChars(Integer.parseInt(emo, 16)));
}
String[] emos = emo.split("_");
char[] char0 = Character.toChars(Integer.parseInt(emos[0], 16));
char[] char1 = Character.toChars(Integer.parseInt(emos[1], 16));
char[] emoji = new char[char0.length + char1.length];
for (int i = 0; i < char0.length; i++) {
emoji[i] = char0[i];
}
for (int i = char0.length; i < emoji.length; i++) {
emoji[i] = char1[i - char0.length];
}
return new String(emoji);
}
由於時間跨度比較長了。年初得時候寫得,基本都記不清了。所以盡量不要來問我額,親們,我只提供解決方案。
代碼我也會上傳一份,有需要得可以拿去耍。
原始碼:http://www.stay4it.com/?p=123
個人辛苦勞動所得,請勿轉載~