Search code examples
androidandroid-edittextspannabletext-styling

Android Spannable: Copy/Cut custom Span in Edittext only Pastes the base class


I am trying to extend some Spans so that they might become compound (to avoid having to set multiple spans on one piece of text), and/or store more information about themselves (like, "types" and "ids", etc.)

Everything works as expected, until I Copy/Cut, then Paste the text. After the Paste operation, the custom spans lose all customisation, only the base-span specific styling remains.

For example, if I extend BackgroundColorSpan to always apply red text colour, it will work. Setting the below Extended BackgroundColorSpan to any text will set the background correctly, and the text will be red, as desired. Here's the code for the span:

public class ExtendedBackgoundColorSpan extends BackgroundColorSpan {

    private final int fgColor = Color.parseColor("#FF0000");

    public ExtendedBackgoundColorSpan(int color) {
        super(color);
    }

    public ExtendedBackgoundColorSpan(Parcel src) {
        super(src);
    }

    /*Make text colour red*/    
    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        ds.setColor(fgColor);
    }
}

All is fine, until I Copy/Cut and then Paste the spanned text. It will then lose its "redness", but retain the background color. Also, the pasted Span is recognised as plain BackgroundColorSpan, and not ExtendedBackgroundColorSpan.

Tried overriding writeToParcel(Parcel dest, int flags) from the base class, with a settable (non-final) fgColor, as well (setting it constructor as well), but nothing worked.

I am also experiencing this behaviour when I try to create custom spans with extra info (like a special tag, or id). The extra informaton, and even the extened type of the span is lost on pasting.

What am I missing?


Edit: This is what I was missing. The following is from Android Developers' ClipData.Item here:

Description of a single item in a ClipData.

The types than an individual item can currently contain are:

  • Text: a basic string of text. This is actually a CharSequence, so it can be formatted text supported by corresponding Android built-in style spans. (Custom application spans are not supported and will be stripped when transporting through the clipboard.)

(Emphasis mine.)

I will leave the accepted anwer accepted, as that was what pointed me in the right direction.

<rant> (Meaning looking at what I might not be able to do, because somebody on the Android team decided I should not. I ended up with a custom EditText, with custom Paste logic, and callbacks for Copy/Cut/Paste actions, just to implement something that shoud have been the job of the OS in the first place. The whole platform feels like a massive hack.)</rant>


Solution

  • You inspired me to have some fun with Spannables. There is no chance to extend BackgroundColorSpan and implement own ParcelableSpan's. Framework doesn't allow it, check it on ParcelableSpan reference. Otherwise I tried to resolve your copy spannable problem and answer is simple:

     SpannableString spannableString = new SpannableString(firstEditText.getText().toString());
     spannableString.setSpan(new BackgroundColorSpan(Color.GREEN), 0, spannableString.length(), 0);
     spannableString.setSpan(new ForegroundColorSpan(Color.RED), 0, spannableString.length(), 0);
    

    String can be copy-paste contains before set span, I've checked it. You can connect those two span to one class and use it with others colors.