IOS的Emoji表情因为编码问题,在Android手机上无法正常显示,如果当前的cc.Label节点使用的是系统字,在系统字库中找不到对应编码的字符,会导致崩溃。
为了解决这个问题,又要兼顾新老版本,要在3个地方做调整:
private static boolean containsEmoji(String source) { if (null == source || 0 == source.length()) { return false; } int len = source.length(); for (int i = 0; i < len; i++) { char c = source.charAt(i); if (isEmojiCharacter(c)) { return true; } } return false; } private static boolean isEmojiCharacter(char c) { return !((c == 0x0) || (c == 0x9) || (c == 0xA) || (c == 0xD) || ((c >= 0x20) && (c <= 0xD7FF)) || ((c >= 0xE000) && (c <= 0xFFFD)) || ((c >= 0x10000) && (c <= 0x10FFFF))); } private static String filterEmoji(String source) { if (!containsEmoji(source)) { return source;// don't contain, just return } StringBuilder buf = null; int len = source.length(); for (int i = 0; i < len; i++) { char c = source.charAt(i); if (!isEmojiCharacter(c)) { if (buf == null) { buf = new StringBuilder(source.length()); } buf.append(c); } } if (buf == null) { return null; } else { if (buf.length() == len) { buf = null; return source; } else { return buf.toString(); } } }
把代码
final byte[] bytesUTF8 = pResult.getBytes("UTF8");
修改为
String text = filterEmoji(pResult); if (null == text || 0 == text.length()) { text = ""; } final byte[] bytesUTF8 = text.getBytes("UTF8");
IOS的Emoji表情的输入有两种方式:一种是Emoji表情页,另一种是输入法打字联想出来的。
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if ([textField isFirstResponder]) { if([[[textField textInputMode] primaryLanguage] isEqualToString:@"emoji"] || ![[textField textInputMode] primaryLanguage]) { return NO; } } ... ... }
1)检测并过滤Emoji表情
// 过滤所有表情。containEmoji为NO表示不含有表情,YES表示含有表情 - (BOOL)stringContainsEmoji:(UITextField *)textField { __block BOOL containEmoji = NO; self.stringAfterFilterEmoji = @""; NSString* string = textField.text; [string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { const unichar hs = [substring characterAtIndex:0]; BOOL is_emoji = NO; // surrogate pair if (0xd800 <= hs && hs <= 0xdbff) { if (substring.length > 1) { const unichar ls = [substring characterAtIndex:1]; const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; if (0x1d000 <= uc && uc <= 0x1f77f) { is_emoji = YES; } } } else if (substring.length > 1) { const unichar ls = [substring characterAtIndex:1]; if (ls == 0x20e3) { is_emoji = YES; } } else { // non surrogate if (0x2100 <= hs && hs <= 0x27ff) { is_emoji = YES; } else if (0x2B05 <= hs && hs <= 0x2b07) { is_emoji = YES; } else if (0x2934 <= hs && hs <= 0x2935) { is_emoji = YES; } else if (0x3297 <= hs && hs <= 0x3299) { is_emoji = YES; } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) { is_emoji = YES; } } if (!is_emoji) { self.stringAfterFilterEmoji = [self.stringAfterFilterEmoji stringByAppendingString:substring]; } containEmoji = is_emoji ? is_emoji : containEmoji; }]; return containEmoji; }
2)应用过滤过的输入
- (BOOL)textFieldShouldEndEditing:(UITextField *)sender { if ([self stringContainsEmoji:sender]) { sender.text = self.stringAfterFilterEmoji; } ... ... }
由于要兼顾老版本,不仅仅要在输入端对Emoji进行过滤,如果老版本使用Emoji,同样要添加过滤;但Lua的编码跟Java和OC有很大不同,Java可以通过char、OC通过unichar可以获取Unicode字符,但Lua不行,Lua是通过string的字节链接的形式来存储Unicode的字符;
通过总结发现,一个Emoji表情的第1个字节(string.byte(s, 1))等于240,第2个字节(string.byte(s,2))等于159的不能被Android识别,从而崩溃,因此可以过滤此种Emoji,从而解决崩溃问题。
function M:getByteCount( byte ) local ret = 0 if byte > 0 and byte <= 127 then ret = 1 elseif byte >= 192 and byte < 223 then ret = 2 elseif byte >= 224 and byte < 239 then ret = 3 elseif byte >= 240 and byte <= 247 then ret = 4 end return ret end function M:filterEmoji( source ) local len = string.len(source) if len < 2 then return source end local ret_str = "" local i = 1 while i <= len do local is_emoji = false local byte_1 = string.byte(source, i) if byte_1 == 240 then local byte_2 = string.byte(source, i + 1) if byte_2 == 159 then is_emoji = true end end local byte_count = self:getByteCount(byte_1) byte_count = byte_count < 1 and 1 or byte_count if not is_emoji then ret_str = ret_str..string.sub(source, i, i + byte_count - 1) end i = i + byte_count end return ret_str end