Paste without rich text formatting into EditText
Asked Answered
Q

4

23

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

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.

Quantum answered 15/7, 2014 at 12:52 Comment(8)
It looks as if the EditText understands rtf formatting as no rtf source text is seen. Please explain. What do you get with editText.getText() ? Formated text?Nyssa
The use case is that someone went to Amazon.com (edit: with Chrome for Android), looked for a book and copy/pasted the title in the EditText field. Then instead of the regular size it gets that big. How do I use the getText() after the paste happened? I can’t do that from the debugger, right?Quantum
????? Make a button or a menu item that does that. Or set an on change listener for the edit text that displays a toast. You could immediately cleanthe text then. addTextChangedListener.Nyssa
You are absolutely right, I was being stupid and thinking too complex instead of pragmatic (I now simply assigned a button to it), but couldn’t solve my problem yet. I updated the question above with the information I got.Quantum
Did you try editText.getText().toString()?Nyssa
That doesn’t work because I would have to set the text again, which would trigger the listener again and again an again.Quantum
But does it come without the spans? Are you sure it would trigger again as the text would not change after the second time.Nyssa
It would come out as a simple string without spans, yes. But as soon as you set it inside the listener it gets triggered again in an infinite loop. The fact that you set it matters and not if the string itself stays the same. I tried that.Quantum
Q
9

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.

For me the only problem was the size of the text. If you have other problems you'd have to adjust what you want to remove.

public void afterTextChanged(Editable string)
{
    CharacterStyle[] toBeRemovedSpans = string.getSpans(0, string.length(),
                                                MetricAffectingSpan.class);
    for (int index = 0; index < toBeRemovedSpans.length; index++)
        string.removeSpan(toBeRemovedSpans[index]);
    }
}
Quantum answered 16/7, 2014 at 9:36 Comment(1)
editText.getText().length() Ohhh thats not nice! Try s.length().Nyssa
K
12

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);
    }
}
Kaltman answered 26/7, 2017 at 6:49 Comment(0)
Q
9

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.

For me the only problem was the size of the text. If you have other problems you'd have to adjust what you want to remove.

public void afterTextChanged(Editable string)
{
    CharacterStyle[] toBeRemovedSpans = string.getSpans(0, string.length(),
                                                MetricAffectingSpan.class);
    for (int index = 0; index < toBeRemovedSpans.length; index++)
        string.removeSpan(toBeRemovedSpans[index]);
    }
}
Quantum answered 16/7, 2014 at 9:36 Comment(1)
editText.getText().length() Ohhh thats not nice! Try s.length().Nyssa
P
3

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.

Perceptible answered 21/8, 2018 at 8:10 Comment(2)
You are using the exact same code as I do though? If you’d want to remove more formatting you’d have to change MetricAffectingSpan.class. Otherwise your Array contains only the Formatting that affects the Metrics of the Span but nothing else.Quantum
Thanks for prompting. I have taken the idea from your code. But missed to paste the exact code I used in my answer. I am now editing it - I used CharacterStyle.class instead of MetricAffectingSpan.classPerceptible
T
0

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();
    }
}
Toshikotoss answered 23/4, 2018 at 16:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.