If you append text to a Spannable it doesn't matter whether that text is just plain text or another Spannable. Spans in the original texts will either expand or not depending on the span's flags (Spannable.SPAN_xyz_INCLUSIVE vs Spannable.SPAN_xyz_EXCLUSIVE). That's why the bold formatting will either expand to the whole string with TextUtils.concat("A", s, "B", s, "C") (using Spannable.SPAN_xyz_INCLUSIVE) or not expand at all (using Spannable.SPAN_xyz_EXCLUSIVE).
Your attempt to append an already formatted Spannable won't work because a span can be used just once.
That's why this:
val s = SpannableStringBuilder("hellohello")
val span = StyleSpan(Typeface.BOLD)
s.setSpan(span, 0, 5, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
s.setSpan(span, 5, 10, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
will result in hellohello, while this:
val s = SpannableStringBuilder("hellohello")
val span1 = StyleSpan(Typeface.BOLD)
val span2 = StyleSpan(Typeface.BOLD)
s.setSpan(span1, 0, 5, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
s.setSpan(span2, 5, 10, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
will result in hellohello
This answers your question:
Why aren't both "hello" in bold?
Unfortunately your question isn't very specific on what you're actually trying to achieve. If it's simply to have a Spannable with AhelloBhelloC, then you now know how. If your goal is to have a generic solution that allows to append arbitrary Spannable with arbitrary spans and keep those spans (CharacterStyle and ParagraphStyle) then you'll have to find a way to clone arbitrary Spanned texts with arbitrary spans (note if s is a SpannableStringBuilder then s.subSequence(0, s.length) copies spans but doesn't clone them). So here's how I'd do it:
private fun Spannable.clone(): Spannable {
val clone = SpannableStringBuilder(toString())
for (span in getSpans(0, length, Any::class.java)) {
if (span is CharacterStyle || span is ParagraphStyle) {
val st = getSpanStart(span).coerceAtLeast(0)
val en = getSpanEnd(span).coerceAtMost(length)
val fl = getSpanFlags(span)
val clonedSpan = cloneSpan(span)
clone.setSpan(clonedSpan, st, en, fl)
}
}
return clone
}
private fun cloneSpan(span: Any): Any {
return when (span) {
is StyleSpan -> StyleSpan(span.style)
// more clone code to be written...
else -> span
}
}
Then you can do:
val s = SpannableString("hello")
s.setSpan(StyleSpan(Typeface.BOLD), 0, s.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = TextUtils.concat(s, " not bold ", s.clone())