75

I divide a spannable object into 3 parts, do different operations, and then I need to merge them.

Spannable str = editText.getText();
Spannable selectionSpannable = new SpannableStringBuilder(str, selectionStart, selectionEnd);
Spannable endOfModifiedSpannable = new SpannableStringBuilder(str, selectionEnd, editText.getText().length());
Spannable beginningOfModifiedSpannable = new SpannableStringBuilder(str, 0, selectionStart);            

How can I do it? I haven't found the required method or constructor to do it.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Eugene
  • 991
  • 1
  • 6
  • 8

5 Answers5

181

You could use this:

TextUtils.concat(span1, span2);

http://developer.android.com/reference/android/text/TextUtils.html#concat(java.lang.CharSequence...)

xil3
  • 16,305
  • 8
  • 63
  • 97
  • 28
    Sometimes, not very often, Java makes things easy. I love those times. +1 – Madbreaks Oct 30 '12 at 22:54
  • 1
    @Madbreaks this is not related to Java, but to Android API. It could be made easy in C++ as well. – Display Name Jul 16 '14 at 06:35
  • 7
    In my case this method had some problems: `TextUtils.concat(span1, " ", span2);` style (ie span) for span1 disappeared, only it's string representation left. Wrapping " " into Spanned didn't help. – marwinXXII Aug 01 '14 at 09:29
  • 4
    Same thing here first spannable loses styling only the second one maintains its style! – Muhammad Alfaifi Oct 27 '14 at 20:02
  • 3
    If your first span style disappeared, it could be because you've used the same `StyleSpan` on both spans. You need to create a new `StyleSpan` for each span. (see [this answer](http://stackoverflow.com/a/5999171/1440076)) – minipif Jul 06 '15 at 07:31
  • 1
    @marwinXXII the `" "` is a `String`, which works with the `CharSequence` interface however it is not a `Spannable`. `new SpannableString(" ")` would work in place of `" "` – HaydenKai Jul 16 '16 at 23:27
  • @marwinXXII I've found a workaround for this in my answer: https://stackoverflow.com/a/49080221/5054192 – nulldev Mar 03 '18 at 03:08
23

Thanks, it works. I have noticed that I can merge even 3 spannable object:

(Spanned) TextUtils.concat(foo, bar, baz)
Eugene
  • 991
  • 1
  • 6
  • 8
9

I know this is old. But after modifying kotlin stdlib a bit I've got this code:

fun <T> Iterable<T>.joinToSpannedString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): SpannedString {
    return joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform)
            .let { SpannedString(it) }
}

Hope it might help somebody.

AlexKost
  • 2,792
  • 4
  • 22
  • 42
8

Use SpannableStringBuilder.

Even better- make a kotlin operator overload:

operator fun Spannable.plus(other: Spannable): Spannable{
    return SpannableStringBuilder(this).append(other)
}

just throw that in any kotlin file as a top level function.

and the you can concatenate using +:

val spanA = ...
val spanB = ...

val concatenatedSpan = spanA + spanB
Mardann
  • 1,953
  • 1
  • 16
  • 23
1

As marwinXXII said in a comment, using TextUtils.concat does work but can cause loss of styles in some cases when you have multiple instances of the same span in a single CharSequence.

A workaround could be to write the CharSequence to a Parcel and then read it back. Example Kotlin extension code to do this below:

fun CharSequence.cloneWithSpans(): CharSequence {
    val parcel = Parcel.obtain()
    TextUtils.writeToParcel(this, parcel, 0)
    parcel.setDataPosition(0)
    val out = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel)
    parcel.recycle()
    return out
}

Example usage of this code:

TextUtils.concat(*yourListOfText.map { it.cloneWithSpans() }.toTypedArray())

Now you can concatenate tons of CharSequences without worrying about losing any of the styles and formatting you have on them!

Note that this will work for most styles, it doesn't work all the time but should be enough to cover all the basic styles.

nulldev
  • 555
  • 6
  • 16
  • Thank you for your answer, I would like to ask a question: What does that `*` do in this situation ? – MBH Oct 10 '22 at 14:11
  • @MBH It's the [spread operator](https://stackoverflow.com/questions/39389003/kotlin-asterisk-operator-before-variable-name-or-spread-operator-in-kotlin) – nulldev Oct 11 '22 at 15:27