Spantastic is an Android library that provides a simple and Kotlin fluent API for creating Android Spannable.
This library wrappers SpannableStringBuilder
and add methods to easily decorate the text with multiple spans.
absoluteSize
align
background
bold
bullet
clickable
drawableMargin
foreground
iconMargin
image
italic
leadingMargin
lineHeight
mask
monospace
quote
relativeSize
sansSerif
scaleX
serif
strike
style
subscript
superscript
tab
textAppearance
typeface
underline
url
Step 1. Add the JitPack repository to your build file
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.wellingtoncabral:android-spantastic:<LATEST-VERSION>'
}
dependencies {
implementation ("com.github.wellingtoncabral:android-spantastic:$LATEST_VERSION")
}
Use the spantastic{}
builder to decorate texts from the SpantasticBuilder
context.
This method will return a SpannableStringBuilder
.
val span = spantastic {
}
binding.textView.text = span
You can add text in many more ways.
val span = spantastic {
+"This is an example using unary plus operator\n"
"This is an example using a string invoke\n" {
// Add decorators
}
text("This is an example using text function\n") {
// Add decorators
}
text(context, R.string.my_string) {
// Add decorators
}
}
binding.textView.text = span
It looks like:
To manipulate the text, add decorators inside the text block.
In the example below, mark the text with multiple decorators/spans.
val span = spantastic {
"Text with bold, underline, absolute size, foreground, and background decorations" {
bold()
underline()
foreground(Color.WHITE)
background(Color.BLACK)
absoluteSize(20)
}
}
binding.textView.text = span
It looks like:
Note that the decorators are applied for a specific block of text. See an example:
val span = spantastic {
+ "Text with "
"bold" {
bold()
}
+ ", "
"relative size" {
relativeSize(2f)
}
+ ", "
"absolute size" {
absoluteSize(22)
}
+ ", "
"foreground" {
foreground(Color.RED)
absoluteSize(18)
}
+ ", and "
"background "{
background(Color.MAGENTA)
foreground(Color.WHITE)
absoluteSize(18)
}
+ "decorations."
}
binding.textView.text = span
It looks like:
If you prefer put full text first and then apply decorators, you can set up the start
and end
position.
Let's take a look at this example that draws the molecular formula of caffeine:
val span = spantastic {
// Example using custom positions
"☕- C8H10N4O2" {
subscript {
start = 4
end = 5
}
subscript {
start = 6
end = 8
}
subscript {
start = 9
end = 10
}
subscript {
start = 11
end = 12
}
}
}
binding.textView.text = span
In the same way without custom positions:
val span = spantastic {
+ "☕- C"
"8" { subscript() }
+ "H"
"10" { subscript() }
+ "N"
"4" { subscript() }
+ "O"
"2" { subscript() }
}
binding.textView.text = span
It looks like:
You can create extension functions to build custom decoration components.
In the example below, we created some custom components: h1
, title
, divider
, and contactList
.
fun SpantasticBuilder.h1(text: String) {
text {
bold()
absoluteSize(22)
align(Layout.Alignment.ALIGN_CENTER)
divider()
}
}
fun SpantasticBuilder.title(text: String, shouldBreakLine: Boolean = true) {
text {
absoluteSize(10)
underline()
bold()
if (shouldBreakLine) newLine()
}
}
fun SpantasticBuilder.divider() {
newLine()
newLine()
}
fun SpantasticBuilder.contactList(
vararg contacts: String,
onClick: (String) -> Unit
) {
contacts.forEachIndexed { index, contact ->
"@$contact" {
clickable { onClick(contact) }
bold()
foreground(Color.MAGENTA)
}
if (index < contacts.size-1) + ", " else + "."
}
}
So, you can use them easily like that:
val span = spantastic {
h1("Example")
title("Contact list: ")
+ "Click to show the contact info "
contactList("Wellington", "Thiago", "Patrick", "Geovana", "Júlia") { contact ->
showContactInfo(contact)
}
divider()
}
binding.textView.text = span
It looks like:
If you already have an instance of SpannableStringBuilder
, you can pass it to the spantastic
builder to be appended in the context. For example, you can pass the editText.text
as SpannableStringBuilder
to the spantastic calling asSpannableStringBuilder
like that:
binding.editText.text = spantastic(binding.editText.text.asSpannableStringBuilder()) {
bold()
}
The bold
decorator will be applied to the existing text in the EditText component.
Spantastic provides 2 ways to work with clickable areas: url
and clickable
.
Take a look in this simple example:
val span = spantastic {
"Text with url" {
url("https://github.com/wellingtoncabral")
}
newLine()
"Text with clickable area" {
clickable(isUnderlineText = true) {
showActivity()
}
}
}
binding.textView.text = span
It looks like:
The first example uses url
to open the brownser with the given url and the second one uses clickable
which invokes the lambda method, in this case showActivity()
.
Let's take a look in a real use case:
val span = spantastic {
"By continuing, you agree to the Terms os Service and Privacy Policy." {
url("https://github.com/wellingtoncabral") {
start = 32
end = 48
}
clickable(
isUnderlineText = true,
init = {
start = 53
end = 67
}
) {
showActivity()
}
}
}
binding.textView.text = span
It looks like:
Spantastic provides many ways to add images.
val span = spantastic {
"Text with image from resource id " {
image(context, R.drawable.ic_android, DynamicDrawableSpan.ALIGN_BASELINE)
}
newLine()
"Text with image " {
image(context, bitmap, DynamicDrawableSpan.ALIGN_CENTER)
}
+ " from the Bitmap "
}
binding.textView.text = span
It looks like:
See also drawableMargin
and iconMargin
.
Use the textAppearance
to set up a Android style resource.
val span = spantastic {
"Text with a custom appearance" {
textAppearance(context, R.style.CustomStyle)
}
}
binding.textView.text = span
<resources>
<style name="CustomStyle">
<item name="android:textColor">@color/teal_700</item>
<item name="android:textSize">18sp</item>
<item name="android:textStyle">bold|italic</item>
</style>
</resources>
It looks like:
val span = spantastic {
"Text with normal alignment" {
align(Layout.Alignment.ALIGN_NORMAL)
}
newLine()
"Text with center alignment" {
align(Layout.Alignment.ALIGN_CENTER)
}
newLine()
"Text with opposite alignment" {
align(Layout.Alignment.ALIGN_OPPOSITE)
}
}
binding.textView.text = span
It looks like:
val span = spantastic {
val myTypeface = Typeface.create(
ResourcesCompat.getFont(context, R.font.aguafina_script), Typeface.NORMAL
)
"Text with a custom typeface" {
typeface(myTypeface)
absoluteSize(24)
}
}
binding.textView.text = span
It looks like:
val span = spantastic {
"Text with a blur mask and absolute size 18" {
mask(BlurMaskFilter(radius = 5f, BlurMaskFilter.Blur.NORMAL))
absoluteSize(18)
}
}
binding.textView.text = span
It looks like:
To see more and use case implementations, please take a look at the sample app.
https://github.com/wellingtoncabral/android-spantastic/tree/main/sample/src/main/java/com/wcabral/spantastic