2

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>

Community
  • 1
  • 1
Attila Orosz
  • 270
  • 4
  • 14

1 Answers1

2

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.

PLNech
  • 3,087
  • 1
  • 23
  • 52
MarcinR
  • 786
  • 1
  • 6
  • 16
  • You're right. I've never suspected `ParcelableSpan` to be non-extendable. :( Too bad. That's why it did not work then, when I tried to directly implement it, along with `UpdateAppearance`, extending straight from `CharacterStyle`. I am currently doing something like you've suggested, what I really wanted is not to have to do this, but also to be able to extend Span classes with additional, non-standard members, like e.g. tags. Would have made my life easier, and my code less messy. :) I have an idea for an ugly workaround, I'll watch for copy/cut events, and re-apply the classes on paste. – Attila Orosz Jul 11 '18 at 13:51
  • Ran out of space above-... :D anyway, re: the above idea of reacting to events, I found this: https://gist.github.com/guillermomuntaner/82491cbf0c88dec560a5 It came from SO, but I only saved the gist, so I cannot credit the origin... – Attila Orosz Jul 11 '18 at 13:57
  • 1
    That would be amazing to create own spannable objects. But you can try implement your own spannable builder with list of spannable objects pass in List, check this class on https://developer.android.com/reference/android/text/style/CharacterStyle – MarcinR Jul 11 '18 at 13:58
  • Yes, that is the basic idea, extend `CharacterClass` and implement all the interfaces you need. You can combine existing spans easily this way, just copy whatever you need from the original sources. (Most of the appearance things are apparently done in `updateDrawState`.) then again, your Spans can store all sort of useful stuff, like IDs, tags, (sub-)stypes, etc. the only issue is the copy/paste thing, really, but using the above Gist, it's quite easy to save the span boundaries in a different buffer, when cutting/copying, and re-apply them at paste time. So I think that's what I'll do – Attila Orosz Jul 11 '18 at 14:03
  • Might even go for the builder, TBH. You deserve a cookie for the idea. :D – Attila Orosz Jul 11 '18 at 14:04
  • Hehe, I'm glad to help you and get some interesting experience :D – MarcinR Jul 11 '18 at 14:06
  • Plus, cookies are worth it. :P – Attila Orosz Jul 11 '18 at 14:06
  • Turns out the real issue was with `ClipboardManager`'s `ClipData.Item`, or at least its idea about formatted texts... See my edit above. – Attila Orosz Jul 11 '18 at 19:25