【问题标题】:EditText with SpannableStringBuilder and ImageSpan doesn't works fine带有 SpannableStringBuilder 和 ImageSpan 的 EditText 不能正常工作
【发布时间】:2013-10-23 06:33:22
【问题描述】:

我正在尝试将表情符号放入 EditText。我已经设法做到了,而且效果很好,但是当我尝试使用软键盘从 EditText 中删除这些表情符号时遇到了问题。我无法通过单击删除按钮来执行此操作。当我插入一个新的 ImageSpan 时,我为它替换了一个 imageId,但是当我尝试删除 de icon 时,我必须在删除图像之前删除所有 imageId 字符。

String fileName = "emoticon1.png";
Drawable d = new BitmapDrawable(getResources(), fileName);
String imageId = "[" + fileName + "]";
int cursorPosition = content.getSelectionStart();
int end = cursorPosition + imageId.length();
content.getText().insert(cursorPosition, imageId);

SpannableStringBuilder ss = new SpannableStringBuilder(content.getText());
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
ss.setSpan(span, cursorPosition, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
content.setText(ss, TextView.BufferType.SPANNABLE);
content.setSelection(end);

我需要通过单击删除按钮来删除表情符号。请问你能帮帮我吗?

谢谢!

【问题讨论】:

  • 如何保存表情符号的开始和结束位置(例如“HashMap”)?!在删除每个字符时,您必须检查该位置是否是哈希图中表情符号的结尾。如果是这样,请删除整个。 (但你必须在表情之前照顾好变化)

标签: android android-edittext galaxy-nexus


【解决方案1】:

这是在EditText 中处理表情符号的实现。此实现使用TextWatcher 来监视EditText 的变化,并检测删除某些文本时是否删除了某些表情符号。

请注意,此实现还验证是否删除了文本选择(不仅是删除键)。

为避免在输入文本时出现文本预测问题,建议将表情符号文本用空格包围(文本预测可以将表情符号文本与相邻文本连接起来)。

package com.takamori.testapp;

import java.util.ArrayList;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

public class MainActivity extends Activity {

    private EmoticonHandler mEmoticonHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText editor = (EditText) findViewById(R.id.messageEditor);
        // Create the emoticon handler.
        mEmoticonHandler = new EmoticonHandler(editor);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_insert_emoticon:
                // WARNING: The emoticon text shall be surrounded by spaces
                // to avoid issues with text prediction.
                mEmoticonHandler.insert(" :-) ", R.drawable.smile);
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private static class EmoticonHandler implements TextWatcher {

        private final EditText mEditor;
        private final ArrayList<ImageSpan> mEmoticonsToRemove = new ArrayList<ImageSpan>();

        public EmoticonHandler(EditText editor) {
            // Attach the handler to listen for text changes.
            mEditor = editor;
            mEditor.addTextChangedListener(this);
        }

        public void insert(String emoticon, int resource) {
            // Create the ImageSpan
            Drawable drawable = mEditor.getResources().getDrawable(resource);
            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);

            // Get the selected text.
            int start = mEditor.getSelectionStart();
            int end = mEditor.getSelectionEnd();
            Editable message = mEditor.getEditableText();

            // Insert the emoticon.
            message.replace(start, end, emoticon);
            message.setSpan(span, start, start + emoticon.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        @Override
        public void beforeTextChanged(CharSequence text, int start, int count, int after) {
            // Check if some text will be removed.
            if (count > 0) {
                int end = start + count;
                Editable message = mEditor.getEditableText();
                ImageSpan[] list = message.getSpans(start, end, ImageSpan.class);

                for (ImageSpan span : list) {
                    // Get only the emoticons that are inside of the changed
                    // region.
                    int spanStart = message.getSpanStart(span);
                    int spanEnd = message.getSpanEnd(span);
                    if ((spanStart < end) && (spanEnd > start)) {
                        // Add to remove list
                        mEmoticonsToRemove.add(span);
                    }
                }
            }
        }

        @Override
        public void afterTextChanged(Editable text) {
            Editable message = mEditor.getEditableText();

            // Commit the emoticons to be removed.
            for (ImageSpan span : mEmoticonsToRemove) {
                int start = message.getSpanStart(span);
                int end = message.getSpanEnd(span);

                // Remove the span
                message.removeSpan(span);

                // Remove the remaining emoticon text.
                if (start != end) {
                    message.delete(start, end);
                }
            }
            mEmoticonsToRemove.clear();
        }

        @Override
        public void onTextChanged(CharSequence text, int start, int before, int count) {
        }

    }
}

【讨论】:

  • "为避免在输入文本时出现文本预测问题,建议将表情符号文本用空格包围(文本预测可以将表情符号文本与相邻文本连接起来)。"这个问题有更好的解决方案吗?
  • 非常感谢这个解决方案。 4 年后仍然有效且有用(考虑到 Android 的变化速度)。
  • 我的 mEmoticonsToRemove 列表中出现 ConcurrentModificationException。似乎之前和之后是从不同的线程调用的,并且几乎立即一种方法没有停止使用列表,而另一种方法被调用。我正在 Android 12 上进行测试。
猜你喜欢
  • 2016-04-26
  • 2023-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
相关资源
最近更新 更多