This is the code that I have.
val s = SpannableString("hello")
s.setSpan(StyleSpan(Typeface.BOLD), 0, s.length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
textView.text = TextUtils.concat(s, s)
The text shown in textView is: hellohello
Why aren't both "hello" in bold?
Edit: I am actually trying to concatenate some SpannableStrings with some normal Strings, something like TextUtils.concat("A", s, "B", s, "C") (the result of which I expect to be AhelloBhelloC). Replacing SPAN_INCLUSIVE_EXCLUSIVE with SPAN_INCLUSIVE_INCLUSIVE solves the "hellohello" problem but not this one.
CodePudding user response:
With SPAN_INCLUSIVE_INCLUSIVE:
After some digging I found TextUtils is using SpannableStringBuilder.append which is using SpannableStringBuilder.replace (https://developer.android.com/reference/android/text/SpannableStringBuilder#replace(int, int, java.lang.CharSequence, int, int)) under the hood and the replace is considered an insert at the end. With SPAN_EXCLUSIVE_INCLUSIVE it would not expand the formatting while it would with SPAN_INCLUSIVE_INCLUSIVE:
If the source contains a span with Spanned#SPAN_PARAGRAPH flag, and it does not satisfy the paragraph boundary constraint, it is not retained.
I also checked the resulting span and printed the start/end of the StyleSpan. The first line with SPAN_EXCLUSIVE_INCLUSIVE the last line with SPAN_INCLUSIVE_INCLUSIVE:
01-14 12:44:11.218 5017 5017 E test : span start/end: 0/5
01-14 12:44:22.575 5079 5079 E test : span start/end: 0/10
CodePudding user response:
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())


