Android Spannable - 表情符号删除 UTF 16
Android Spannable - Emoji deletion UTF 16
我对软键盘连接和 ankushsachdeva 表情符号键盘的自定义实现有问题,带有表情符号图像。
https://github.com/ankushsachdeva/emojicon
当我插入一些 UTF 16 格式的新表情符号时,就像这个家庭笑脸一样:http://www.unicode.org/Public/emoji/2.0//emoji-zwj-sequences.txt
或者不同肤色的笑脸。 unicode 被我的 ImageSpans 正确替换了。
当我在表情符号键盘上按下我的特殊后退按钮时,它会通过调用键事件删除完整的表情符号。
mEmojiView.setOnEmojiconBackspaceClickedListener(new EmojiView.OnEmojiconBackspaceClickedListener() {
@Override
public void onEmojiconBackspaceClicked(View v) {
KeyEvent event = new KeyEvent(
0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
send_text.dispatchKeyEvent(event);
}
});
但问题来了:
当我尝试按下默认键盘上的键盘退格键时,它会逐个字符地删除而不是整个表情符号。
并且由于较新的笑脸由多个 chars/unicode 替代品组成,我不得不多次按下退格按钮并面对一些奇怪的其他笑脸组合。
当我尝试通过长按 EditText 来 select 表情符号时,甚至会出现一些奇怪的行为,它只是 select 那个较长的表情符号的第一个字符,但标记了整个 ImageSpan .
有什么解决方法的建议吗?
这是因为软键盘的工作原理。他们中很少有人使用关键事件。他们更有可能使用 deleteSurroundingText 进行删除。您需要做的是为您的视图覆盖 inputConnection,并覆盖 deleteSurrounding 文本,这样如果删除区域中有表情符号,它会删除整个表情符号,而不仅仅是单个字符。
感谢 Gabe Sechan 的帮助。
这是一些对我有用的代码。随时添加改进。
修复自定义 EditText 中的选择:
LinkedList<EmojiData.EmojiTupel> mEmojis = new LinkedList<>();
boolean fromReselecting = false;
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
//Log.d("Selection changed", selStart + " " + selEnd + " length: " + getText().length());
if(!fromReselecting && selStart != selEnd){
EmojiData.EmojiTupel toFix = inBetweenEmoji(selStart, selEnd);
if(toFix != null){
fromReselecting = true;
setSelection(toFix.start, toFix.end);
}
}
else{
fromReselecting = false;
}
}
private EmojiData.EmojiTupel inBetweenEmoji(int selStart, int selEnd){
if(mEmojis == null){
return null;
}
for (EmojiData.EmojiTupel tupel: mEmojis) {
if((tupel.start < selStart && selEnd <= tupel.end) ||
(tupel.start <= selStart && selEnd < tupel.end)
){
//Log.d("InBetween ", "Selection: " + selStart + " " + selEnd + " Emoji: " + tupel.start + " "+ tupel.end );
return tupel;
}
}
return null;
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
mEmojis = EmojiUtils.insertEmojis(getContext(), getText(), mEmojiconSize);
}
还有在应该删除文本时使用 KeyEvents 的自定义 InputConnection。使用多个表情符号效果很好,因为选择是固定的。
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new EmojiInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class EmojiInputConnection extends InputConnectionWrapper {
public EmojiInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (beforeLength == 1 && afterLength == 0) {
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
} else{
int cursorPos = getSelectionStart();
int cursorEnd = getSelectionEnd();
if(cursorEnd == cursorPos && beforeLength == 2 && afterLength == 0){
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
其中 EmojiTupel 是来自另一个 class 的字符串中索引的包装器。
public static class EmojiTupel{
public int start, end;
public EmojiTupel(int start, int end){
this.start = start;
this.end = end;
}
}
我对软键盘连接和 ankushsachdeva 表情符号键盘的自定义实现有问题,带有表情符号图像。
https://github.com/ankushsachdeva/emojicon
当我插入一些 UTF 16 格式的新表情符号时,就像这个家庭笑脸一样:http://www.unicode.org/Public/emoji/2.0//emoji-zwj-sequences.txt
或者不同肤色的笑脸。 unicode 被我的 ImageSpans 正确替换了。
当我在表情符号键盘上按下我的特殊后退按钮时,它会通过调用键事件删除完整的表情符号。
mEmojiView.setOnEmojiconBackspaceClickedListener(new EmojiView.OnEmojiconBackspaceClickedListener() {
@Override
public void onEmojiconBackspaceClicked(View v) {
KeyEvent event = new KeyEvent(
0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
send_text.dispatchKeyEvent(event);
}
});
但问题来了:
当我尝试按下默认键盘上的键盘退格键时,它会逐个字符地删除而不是整个表情符号。
并且由于较新的笑脸由多个 chars/unicode 替代品组成,我不得不多次按下退格按钮并面对一些奇怪的其他笑脸组合。
当我尝试通过长按 EditText 来 select 表情符号时,甚至会出现一些奇怪的行为,它只是 select 那个较长的表情符号的第一个字符,但标记了整个 ImageSpan .
有什么解决方法的建议吗?
这是因为软键盘的工作原理。他们中很少有人使用关键事件。他们更有可能使用 deleteSurroundingText 进行删除。您需要做的是为您的视图覆盖 inputConnection,并覆盖 deleteSurrounding 文本,这样如果删除区域中有表情符号,它会删除整个表情符号,而不仅仅是单个字符。
感谢 Gabe Sechan 的帮助。
这是一些对我有用的代码。随时添加改进。
修复自定义 EditText 中的选择:
LinkedList<EmojiData.EmojiTupel> mEmojis = new LinkedList<>();
boolean fromReselecting = false;
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
//Log.d("Selection changed", selStart + " " + selEnd + " length: " + getText().length());
if(!fromReselecting && selStart != selEnd){
EmojiData.EmojiTupel toFix = inBetweenEmoji(selStart, selEnd);
if(toFix != null){
fromReselecting = true;
setSelection(toFix.start, toFix.end);
}
}
else{
fromReselecting = false;
}
}
private EmojiData.EmojiTupel inBetweenEmoji(int selStart, int selEnd){
if(mEmojis == null){
return null;
}
for (EmojiData.EmojiTupel tupel: mEmojis) {
if((tupel.start < selStart && selEnd <= tupel.end) ||
(tupel.start <= selStart && selEnd < tupel.end)
){
//Log.d("InBetween ", "Selection: " + selStart + " " + selEnd + " Emoji: " + tupel.start + " "+ tupel.end );
return tupel;
}
}
return null;
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
mEmojis = EmojiUtils.insertEmojis(getContext(), getText(), mEmojiconSize);
}
还有在应该删除文本时使用 KeyEvents 的自定义 InputConnection。使用多个表情符号效果很好,因为选择是固定的。
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new EmojiInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class EmojiInputConnection extends InputConnectionWrapper {
public EmojiInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (beforeLength == 1 && afterLength == 0) {
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
} else{
int cursorPos = getSelectionStart();
int cursorEnd = getSelectionEnd();
if(cursorEnd == cursorPos && beforeLength == 2 && afterLength == 0){
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
其中 EmojiTupel 是来自另一个 class 的字符串中索引的包装器。
public static class EmojiTupel{
public int start, end;
public EmojiTupel(int start, int end){
this.start = start;
this.end = end;
}
}