我将TextWatcher添加到EditText字段,以将表情符号转换为表情符号。当我键入一个空格,然后输入表情符号:)时,它将立即转换为微笑表情符号,并且将光标放置在表情符号之后。然后我删除表情符号。使用模拟器上的桌面键盘,一切都可以正常工作。但是,当我在模拟器或设备上使用Android键盘时,删除操作会导致出现一个未知字符来代替表情符号,该表情符号也必须删除。我知道表情符号会占用额外的字符,但是我不明白为什么一个键盘与另一个键盘会发生这种情况?我该如何解决?
期望 Input
-> Output
:
hi :)
-> hi 😊
hi :(
-> hi 😞
hi:)
-> hi:)
hi :)
-> hi 😊
->删除-> hi<space>
屏幕截图删除表情符号后无法识别的字符
代码
EmojiTextWatcher.class
/**
* Follows the following principles:
* 1. When an emoticon is added and a space is typed following it, that emoticon can be converted to
* emoji if it has a space preceding it, or it is at the beginning.
* 2. When an emoji is deleted AND it was the last converted emoticon, emoticons will not be
* converted until another space is typed.
*/
public class EmojiTextWatcher implements TextWatcher {
public static final String TAG = EmojiTextWatcher.class.getName();
private EditText mEditText;
private boolean allowNextEmoji = true;
@Nullable
private Integer lastEmojiIndex = null;
private int selectorIndex;
public EmojiTextWatcher(EditText editText) {
mEditText = editText;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
selectorIndex = mEditText.getSelectionEnd();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
int newSelectorIndex = mEditText.getSelectionEnd();
if (newSelectorIndex - selectorIndex == 1) { // one character added
if (allowNextEmoji) {
int startOfWordIndex = getPreviousSpaceInTextFromIndex(s, selectorIndex) + 1;
int endOfWordIndex = newSelectorIndex;
String possibleEmoji = getEmojiFromSubstring(s, startOfWordIndex, endOfWordIndex);
if (possibleEmoji != null) { // word was an emoticon
// remove then re-add listener so this method isn't called again when updating text
mEditText.removeTextChangedListener(this);
Editable oldText = mEditText.getText();
oldText.replace(startOfWordIndex, endOfWordIndex, possibleEmoji);
lastEmojiIndex = startOfWordIndex;
mEditText.addTextChangedListener(this);
}
} else if (s.charAt(selectorIndex) == ' ') { // character was a space
allowNextEmoji = true;
}
} else if (newSelectorIndex - selectorIndex < 0) { // deletion of text
if (lastEmojiIndex != null &&
lastEmojiIndex >= newSelectorIndex) { // deleted the last converted emoji
allowNextEmoji = false;
lastEmojiIndex = null;
}
}
}
private String getEmojiFromSubstring(CharSequence sequence, int startIndex, int endIndex) {
return getEmojiFromSubstring(sequence.toString(), startIndex, endIndex);
}
private String getEmojiFromSubstring(String text, int startIndex, int endIndex) {
String word = text.substring(startIndex, endIndex);
return Emoji.getEmojiFromEmoticon(word);
}
/**
* Gets the previous space index in the text, or -1 if there are no previous spaces.
*
* @param s the charsequence to look through
* @param lookBeforeIndex index to look for previous space before
* @return the index of the previous space
*/
private int getPreviousSpaceInTextFromIndex(CharSequence s, int lookBeforeIndex) {
return s.subSequence(0, lookBeforeIndex).toString().lastIndexOf(' ');
}
}
Emoji.class
public class Emoji {
private static class ReplacementsMap extends HashMap<String,Integer> {
private static ReplacementsMap mInstance;
private ReplacementsMap() {
super();
put(":)", 0x1F60A);
put(":(", 0x1F61E);
put("<3", 0x2764);
}
public static ReplacementsMap getInstance() {
if (mInstance == null) {
mInstance = new ReplacementsMap();
}
return mInstance;
}
}
public static String getEmojiFromEmoticon(String possibleEmoticon) {
Integer possibleEmoji = ReplacementsMap.getInstance().get(possibleEmoticon);
if (possibleEmoji != null) {
return getUnicodeChar(possibleEmoji);
}
return null;
}
private static String getUnicodeChar(int codepoint) {
return new String(Character.toChars(codepoint));
}
}
messageText.setText(oldText.replace(startOfWordIndex, endOfWordIndex, possibleEmoji));
messageText.setSelection(messageText.getText().length());
我刚用过setText
并替换。