Paste without rich text formatting into EditText

2019-02-12 10:46发布

问题:

If I copy/paste text from Chrome for Android into my EditText view it gets messed up, apparently due to rich text formatting.

The font size is totally messed up and not that big http://erikmi.tk/WaeG/image.png

Is there a way to tell the EditText view to ignore rich text formatting? Or can I catch the paste event and remove it before it gets set? How would I do that?

UPDATE: So I realized that the editText.getText() gives me a SpannableString that contains some formatting. I can get rid of that by calling .clearSpans(); on it. BUT I cannot do anything like that in editText.addTextChangedListener(new TextWatcher() { … } because it gets terribly slow and the UI only updates when I leave the editText view.

回答1:

The problem with clearSpans() was that it removed too much and the editText behaves weird thereafter. By following the approach in this answer I only remove the MetricAffectingSpan and it works fine then.

public void afterTextChanged(Editable string)
{
    CharacterStyle[] toBeRemovedSpans = string.getSpans(0, string.length(),
                                                MetricAffectingSpan.class);
    for (int index = 0; index < toBeRemovedSpans; index++)
        string.removeSpan(toBeRemovedSpans[index]);
    }
}


回答2:

A perfect and easy way: Override the EditText's onTextContextMenuItem and intercept the android.R.id.paste to be android.R.id.pasteAsPlainText

@Override
public boolean onTextContextMenuItem(int id) {
    if (id == android.R.id.paste) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            id = android.R.id.pasteAsPlainText;
        } else {
            onInterceptClipDataToPlainText();
        }
    }
    return super.onTextContextMenuItem(id);
}


private void onInterceptClipDataToPlainText() {
    ClipboardManager clipboard = (ClipboardManager) getContext()
        .getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData clip = clipboard.getPrimaryClip();
    if (clip != null) {
        for (int i = 0; i < clip.getItemCount(); i++) {
            final CharSequence paste;
            // Get an item as text and remove all spans by toString().
            final CharSequence text = clip.getItemAt(i).coerceToText(getContext());
            paste = (text instanceof Spanned) ? text.toString() : text;
            if (paste != null) {
                ClipBoards.copyToClipBoard(getContext(), paste);
            }
        }
    }
}

And the copyToClipBoard:

public class ClipBoards {

    public static void copyToClipBoard(@NonNull Context context, @NonNull CharSequence text) {
        ClipData clipData = ClipData.newPlainText("rebase_copy", text);
        ClipboardManager manager = (ClipboardManager) context
            .getSystemService(Context.CLIPBOARD_SERVICE);
        manager.setPrimaryClip(clipData);
    }
}


回答3:

This simple copy and paste should give you text without formatting:

public void paste(View v) {
    int sdk = android.os.Build.VERSION.SDK_INT;
    if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
        android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        editText.setText(clipboard.getText());
    } else {
        android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        android.content.ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);

        if (item.getText() != null) {
            editText.getText().insert(editText.getSelectionStart(), item.getText());
        }
    }
    editText.setSelection(0);
}

public void copy(View v) {
    if (editText.getText() != null) {
        String selectedText = editText.getText().toString();

        int start = editText.getSelectionStart();
        int end = editText.getSelectionEnd();

        if (end > start) {
            selectedText = selectedText.substring(start, end);

            int sdk = android.os.Build.VERSION.SDK_INT;
            if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
                android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                clipboard.setText(selectedText);
            } else {
                android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                android.content.ClipData clip = android.content.ClipData.newPlainText("WordKeeper", selectedText);
                clipboard.setPrimaryClip(clip);
            }
        } else
            Toast.makeText(this, "To copy, select some text first by pressing and and holding the text area.", Toast.LENGTH_SHORT).show();
    }
}


回答4:

Erik's answer above removes few formatting, but not all. Hence I used:

CharacterStyle[] toBeRemovedSpans = string.getSpans(0, string.length(), CharacterStyle.class); to remove all formatting.