Skip to content

Instantly share code, notes, and snippets.

@JakeWharton
Last active June 9, 2023 07:35
Show Gist options
  • Save JakeWharton/11274467 to your computer and use it in GitHub Desktop.
Save JakeWharton/11274467 to your computer and use it in GitHub Desktop.
Extremely simple wrapper around SpannableStringBuilder to make the API more logical and less awful. Apache 2 licensed.
import android.text.SpannableStringBuilder;
import java.util.ArrayDeque;
import java.util.Deque;
import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
/** A {@link SpannableStringBuilder} wrapper whose API doesn't make me want to stab my eyes out. */
public class Truss {
private final SpannableStringBuilder builder;
private final Deque<Span> stack;
public Truss() {
builder = new SpannableStringBuilder();
stack = new ArrayDeque<>();
}
public Truss append(String string) {
builder.append(string);
return this;
}
public Truss append(CharSequence charSequence) {
builder.append(charSequence);
return this;
}
public Truss append(char c) {
builder.append(c);
return this;
}
public Truss append(int number) {
builder.append(String.valueOf(number));
return this;
}
/** Starts {@code span} at the current position in the builder. */
public Truss pushSpan(Object span) {
stack.addLast(new Span(builder.length(), span));
return this;
}
/** End the most recently pushed span at the current position in the builder. */
public Truss popSpan() {
Span span = stack.removeLast();
builder.setSpan(span.span, span.start, builder.length(), SPAN_INCLUSIVE_EXCLUSIVE);
return this;
}
/** Create the final {@link CharSequence}, popping any remaining spans. */
public CharSequence build() {
while (!stack.isEmpty()) {
popSpan();
}
return builder; // TODO make immutable copy?
}
private static final class Span {
final int start;
final Object span;
public Span(int start, Object span) {
this.start = start;
this.span = span;
}
}
}
@yogeshnarayanan
Copy link

Thanks Jake, Truss saves time and you are great.

Snippet (for basic learner like me) to replicate the above styled string with Truss

Truss t = new Truss()
.append("Thanks Jake, ")
.pushSpan(new StyleSpan(android.graphics.Typeface.BOLD))
.append("Truss saves time")
.popSpan()
.append(" and you are great.");
TextView name = (TextView)findViewById(R.id.tvname);
name.setText(t.build());

@PaulWoitaschek
Copy link

In kotlin the thing get even easier

inline fun SpannableStringBuilder.withSpan(span: Any, action: SpannableStringBuilder.() -> Unit): SpannableStringBuilder {
  val from = length
  action()
  setSpan(span, from, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
  return this
}

Which just lets you use what's already there:

SpannableStringBuilder("Thanks Jetbrains, ")
  .withSpan(StyleSpan(android.graphics.Typeface.BOLD)) { append("Kotlin saves time") }
  .append(" and is great.")

@juanes30
Copy link

juanes30 commented Oct 6, 2017

Thanks Jake!!!!

@2dxgujun
Copy link

@ramakrishnajoshi
Copy link

ramakrishnajoshi commented Dec 30, 2019

Thanks Jake, Truss saves time and you are great.

Snippet (for basic learner like me) to replicate the above styled string with Truss

Truss t = new Truss()
.append("Thanks Jake, ")
.pushSpan(new StyleSpan(android.graphics.Typeface.BOLD))
.append("Truss saves time")
.popSpan()
.append(" and you are great.");
TextView name = (TextView)findViewById(R.id.tvname);
name.setText(t.build());

//Note to future self
t.build() returns charSequence. I observed that using toString() removes StyleSpans applied like bold i.e by using name.setText(t.build().toString);

So don't toString() the charSequence. :)

    val styledText = Truss()
            .pushSpan(StyleSpan(Typeface.BOLD))
            .append("This should be bold text\n")
            .popSpan()
            .append("This should be normal text\n")
            .pushSpan(ForegroundColorSpan(Color.BLUE))
            .append("This should be Blue text")
            .popSpan()
            .pushSpan(UnderlineSpan())
            .append("This should be Underlined text")
            .popSpan()
            .pushSpan(AbsoluteSizeSpan(16, true))
            .append("This should be text with size 16")
            .popSpan()
            .build()

@captswag
Copy link

@captswag
Copy link

captswag commented May 1, 2021

Thanks @JakeWharton. Those links are helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment