这一次来说说第二种方案。
首先来分析一下:
1. 我们要做的是让客户端支持emoji表情,而且有多个不同的客户端,比如android,ios,还有网页。
2. 那么从服务端来解决我觉得不是一个很好的方案,虽然可以对不同客户端分别处理,但是要明白一点,不管服务器的存储方式,客户端还是需要显示的。
3. 第二种方案是,用图片代替这个表情字符,这样子会比较好看,用户体验好一些。所以客户端是必然需要做处理的,而转换成一些标记也只是需要一个转换函数和一个逆转函数。而且我认为客户端分治方法要比服务器全盘处理好一些。
所以这一种方案的结果是,所有内容都放在客户端处理。服务器不做任何调整,这样修改的东西少一些。主要是服务器修改起来会更加费事。
解决方案:
由于有githug上发现有人已经实现了,所以我也就拿来主义了,不过把源码看了一遍,写得很好,肯定比我写的好,不过实现的想法是一样的。
githug Emojicon : https://github.com/rockerhieu/emojicon 也可以在这里下载:http://pan.baidu.com/s/1sjlsRGL
里面有个demo可以直接使用。
简单来说原理:
一个emoji表情对应一个utf8mb4字符。
一个emoji表情对应一张图片。
那么我们要做的就是,当这个字符被输入的时候,用一张图片代替。在android中Spannable可以显示很多东西,显示图片是没有问题的。
这是个体力活。还好有人已经做完了。在此感谢!
再说说数据库存储方面。
刚才已经说了,服务端不做任何修改,那么就需要在客户端把utf8mb4的编码转换一下,转换成普通字符,至少是普通的utf8,比如emoji字符 0x1f604 可以转换成[emoji]0x1f604[/emoji],这样就可以存储了,显示的时候再转换回去。
下面是我修改的部分,即,存入数据库前的转换和显示的时候的逆转换
private static final String emojiSeparatorStart = String.valueOf(Character.toChars(1));
private static final String emojiSeparatorEnd = "/" + String.valueOf(Character.toChars(1));
/**
* Convert emoji characters of the given Spannable to the according
* emojiCode for save for database.
*
* @author wuwang(xzpv444@gmail.com)
*
* @param context
* @param text
*/
public static String convertEmojis(Context context, Spannable text) {
String result = "";
int index = 0;
int length = -1;
int textLength = text.length();
int textLengthToProcessMax = textLength - index;
int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length + index);
// remove spans throughout all text
EmojiconSpan[] oldSpans = text.getSpans(0, textLength, EmojiconSpan.class);
for (int i = 0; i < oldSpans.length; i++) {
text.removeSpan(oldSpans[i]);
}
int skip;
for (int i = index; i < textLengthToProcess; i += skip) {
skip = 0;
int icon = 0;
char c = text.charAt(i);
if (isSoftBankEmoji(c)) {
icon = getSoftbankEmojiResource(c);
skip = icon == 0 ? 0 : 1;
}
int unicode = 0;
if (icon == 0) {
unicode = Character.codePointAt(text, i);
skip = Character.charCount(unicode);
if (unicode > 0xff) {
icon = getEmojiResource(context, unicode);
}
if (icon == 0 && i + skip < textLengthToProcess) {
int followUnicode = Character.codePointAt(text, i + skip);
if (followUnicode == 0x20e3) {
int followSkip = Character.charCount(followUnicode);
switch (unicode) {
case 0x0031:
icon = R.drawable.emoji_0031;
break;
case 0x0032:
icon = R.drawable.emoji_0032;
break;
case 0x0033:
icon = R.drawable.emoji_0033;
break;
case 0x0034:
icon = R.drawable.emoji_0034;
break;
case 0x0035:
icon = R.drawable.emoji_0035;
break;
case 0x0036:
icon = R.drawable.emoji_0036;
break;
case 0x0037:
icon = R.drawable.emoji_0037;
break;
case 0x0038:
icon = R.drawable.emoji_0038;
break;
case 0x0039:
icon = R.drawable.emoji_0039;
break;
case 0x0030:
icon = R.drawable.emoji_0030;
break;
case 0x0023:
icon = R.drawable.emoji_0023;
break;
default:
followSkip = 0;
break;
}
skip += followSkip;
} else {
int followSkip = Character.charCount(followUnicode);
switch (unicode) {
case 0x1f1ef:
icon = (followUnicode == 0x1f1f5) ? R.drawable.emoji_1f1ef_1f1f5 : 0;
break;
case 0x1f1fa:
icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1fa_1f1f8 : 0;
break;
case 0x1f1eb:
icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1eb_1f1f7 : 0;
break;
case 0x1f1e9:
icon = (followUnicode == 0x1f1ea) ? R.drawable.emoji_1f1e9_1f1ea : 0;
break;
case 0x1f1ee:
icon = (followUnicode == 0x1f1f9) ? R.drawable.emoji_1f1ee_1f1f9 : 0;
break;
case 0x1f1ec:
icon = (followUnicode == 0x1f1e7) ? R.drawable.emoji_1f1ec_1f1e7 : 0;
break;
case 0x1f1ea:
icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1ea_1f1f8 : 0;
break;
case 0x1f1f7:
icon = (followUnicode == 0x1f1fa) ? R.drawable.emoji_1f1f7_1f1fa : 0;
break;
case 0x1f1e8:
icon = (followUnicode == 0x1f1f3) ? R.drawable.emoji_1f1e8_1f1f3 : 0;
break;
case 0x1f1f0:
icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1f0_1f1f7 : 0;
break;
default:
followSkip = 0;
break;
}
skip += followSkip;
}
}
}
if (icon > 0) {
result += emojiSeparatorStart + Integer.toHexString(unicode) + emojiSeparatorEnd;
} else {
result += c;
}
}
return result;
}
/**
* restore emojiCode to emoji character
*
* @author wuwang(xzpv444@gmail.com)
*
* @param context
* @param text
* @return
*/
public static CharSequence restoreEmojis(Context context, CharSequence text) {
String regexp = emojiSeparatorStart + ".*?" + emojiSeparatorStart;
Pattern p = Pattern.compile(regexp);
Matcher m = p.matcher(text);
while (true) {
if (m.find()) {
String x = String.valueOf(Character.toChars(Integer.valueOf(m.group(0).replace(emojiSeparatorEnd, "").replace(emojiSeparatorStart, ""), 16)));
text = text.toString().replace(m.group(0), x);
} else {
break;
}
}
return text;
}