How to set multiple spans on a TextView's text on the same partial text?
Solution 1
Simply set additional spans. They are going to overlap/merge when neccessary. This code works for me:
final SpannableString text = new SpannableString("Hello stackOverflow");
text.setSpan(new RelativeSizeSpan(1.5f), text.length() - "stackOverflow".length(), text.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new ForegroundColorSpan(Color.RED), 3, text.length() - 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(text);
Solution 2
I know this a new reply to an already answered question but I'd like to share a utility class I made which makes this task easier.
Java Version
public class SimpleSpanBuilder {
private class SpanSection{
private final String text;
private final int startIndex;
private final CharacterStyle[] styles;
private SpanSection(String text, int startIndex,CharacterStyle... styles){
this.styles = styles;
this.text = text;
this.startIndex = startIndex;
}
private void apply(SpannableStringBuilder spanStringBuilder){
if (spanStringBuilder == null) return;
for (CharacterStyle style : styles){
spanStringBuilder.setSpan(style, startIndex, startIndex + text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}
private List<SpanSection> spanSections;
private StringBuilder stringBuilder;
public SimpleSpanBuilder(){
stringBuilder = new StringBuilder();
spanSections = new ArrayList<>();
}
public SimpleSpanBuilder append(String text,CharacterStyle... styles){
if (styles != null && styles.length > 0) {
spanSections.add(new SpanSection(text, stringBuilder.length(),styles));
}
stringBuilder.append(text);
return this;
}
public SimpleSpanBuilder appendWithSpace(String text,CharacterStyle... styles){
return append(text.concat(" "),styles);
}
public SimpleSpanBuilder appendWithLineBreak(String text,CharacterStyle... styles){
return append(text.concat("\n"),styles);
}
public SpannableStringBuilder build(){
SpannableStringBuilder ssb = new SpannableStringBuilder(stringBuilder.toString());
for (SpanSection section : spanSections){
section.apply(ssb);
}
return ssb;
}
@Override
public String toString() {
return stringBuilder.toString();
}
}
Usage:
SimpleSpanBuilder ssb = new SimpleSpanBuilder();
ssb.appendWithSpace("Hello");
ssb.append("StackOverflow",new ForegroundColorSpan(Color.RED),new RelativeSizeSpan(1.5));
textView.setText(ssb.build());
Kotlin Version
class SimpleSpanBuilder() {
class Span {
private var startIndex: Int = 0
internal var text: String
private var styles: Array<out CharacterStyle>
internal constructor(index: Int, text: String, vararg styles: CharacterStyle) {
this.startIndex = index
this.text = text
this.styles = styles
}
constructor(text: String, vararg styles: CharacterStyle) : this(0, text, *styles)
internal fun setIndex(index: Int): Span {
return Span(index, this.text, *this.styles)
}
internal fun apply(spanStringBuilder: SpannableStringBuilder?) {
if (spanStringBuilder == null) return
for (style in styles) {
spanStringBuilder.setSpan(
style,
startIndex,
startIndex + text.length,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
}
private val spanSections = mutableListOf<Span>()
private val stringBuilder = StringBuilder()
constructor(text: String, vararg styles: CharacterStyle) : this() {
plus(Span(text, *styles))
}
operator fun plus(span: SimpleSpanBuilder.Span): SimpleSpanBuilder {
spanSections.add(span.setIndex(stringBuilder.length))
stringBuilder.append(span.text)
return this
}
fun build(): SpannableStringBuilder {
val ssb = SpannableStringBuilder(stringBuilder.toString())
for (section in spanSections) {
section.apply(ssb)
}
return ssb
}
override fun toString(): String {
return stringBuilder.toString()
}
}
Usage
var ssb = SimpleSpanBuilder("Hello ",ForegroundColorSpan(Color.BLUE))
ssb += SimpleSpanBuilder.Span(
"StackOverflow",
ForegroundColorSpan(Color.RED),
RelativeSizeSpan(1.5f)
)
textView.text = ssb.build()
Solution 3
Most Easy Way?
textView.setText("I love coding");
setHighLightedText(textView,"coding");
Just use below method -
public void setHighLightedText(TextView tv, String textToHighlight) {
String tvt = tv.getText().toString();
int ofe = tvt.indexOf(textToHighlight, 0);
Spannable wordToSpan = new SpannableString(tv.getText());
for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
ofe = tvt.indexOf(textToHighlight, ofs);
if (ofe == -1)
break;
else {
// you can change or add more span as per your need
wordToSpan.setSpan(new RelativeSizeSpan(2f), ofe,ofe + textToHighlight.length(), 0); // set size
wordToSpan.setSpan(new ForegroundColorSpan(Color.RED), ofe, ofe + textToHighlight.length(), 0);// set color
tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
}
}
}
Solution 4
Kotlin can help doing this with an extension on SpannableStringBuilder
:
fun SpannableStringBuilder.spansAppend(
text: CharSequence,
flags: Int,
vararg spans: Any
): SpannableStringBuilder {
val start = length
append(text)
spans.forEach { span ->
setSpan(span, start, length, flags)
}
return this
}
Examples of usage:
val builder = SpannableStringBuilder()
builder.append("Start of string ")
builder.spansAppend(
"text spanned",
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
RelativeSizeSpan(1.1f),
ForegroundColorSpan(Color.RED)
)
![android developer](https://i.stack.imgur.com/D5dZW.jpg?s=256&g=1)
android developer
Really like to develop Android apps & libraries on my spare time. Github website: https://github.com/AndroidDeveloperLB/ My spare time apps: https://play.google.com/store/apps/developer?id=AndroidDeveloperLB
Updated on July 05, 2022Comments
-
android developer almost 2 years
Suppose I have the next text :
Hello stackOverflow
And I wish to set the second word to be both RelativeSizeSpan (to set a relative font size) and TextAppearanceSpan (to set the color of the text) , how do I merge them both ?
All I know is that I can choose one of them , using the next code for example :
final SpannableString textToShow = new SpannableString("Hello stackOverflow"); textToShow.setSpan(new RelativeSizeSpan(1.5f), textToShow.length() - "stackOverflow".length(),textToShow.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(textToShow);
But I need to also set the color , or even add other features from other spanning classes ...
What can I do ?
-
Zielony over 11 yearsrandom number I had in my head. It's here only to show that you can have multiple spans intersecting each other
-
Arda Kara over 9 yearsbut how can I add more than one ForegroundcolorSpan for instance?
-
android developer over 8 yearsThis is actually a nice solution. +1 for the effort.
-
android developer over 6 years@Zielony What if I have made a new class ? Should it expand ReplacementSpan or something else, so that I will be able to put multiple of them on the same text?
-
Zielony over 6 yearsAs far as I know, the algorithm doesn't care about the types
-
Prinkal Kumar over 6 yearshow to set click listener on texts
-
W.K.S over 6 years@PrinkalKumar Try the updated answer with
ClickableSpan
-
android developer about 6 yearsThis is nice, but I've already accepted an answer which worked fine. However, I will give you +1 for the effort.
-
android developer almost 6 yearsCan you please show a sample or two of how to use your code? I'm not sure I understand what it does...
-
Kevin Robatel almost 6 years@androiddeveloper I have added an example
-
android developer almost 6 yearsOh this is very nice. Thank you for your work and effort. Could be very helpful some day. I hope I will check it out again when I need it. Such short code for such useful thing. I will give you +1 for this.
-
android developer almost 6 yearsWait, this is an already existing function? Someone wrote me this today: stackoverflow.com/a/52331300/878126
-
Kevin Robatel almost 6 yearsDon't know this. inSpan is from Android-KTX github.com/android/android-ktx/blob/master/src/main/java/…
-
Neeraj over 5 yearsA detailed example can be found in this link
-
Bugs Happen over 4 yearsAlso note that
ClickableSpan
won't work withouttextView.movementMethod = LinkMovementMethod.getInstance()
-
Bugs Happen over 4 yearsAlso note that
ClickableSpan
won't work withouttextView.movementMethod = LinkMovementMethod.getInstance()
-
famfamfam about 3 yearshi, did u support image?
-
Ensar Bayhan over 2 yearsThis should be the accepted answer! Working like a charm, thanks!