Skip to content

Instantly share code, notes, and snippets.

@radoyankov
Last active January 27, 2023 08:59
Show Gist options
  • Save radoyankov/29833fc1f5ecd577b0581d6de93ff60f to your computer and use it in GitHub Desktop.
Save radoyankov/29833fc1f5ecd577b0581d6de93ff60f to your computer and use it in GitHub Desktop.
Easy Spannable on Kotlin
val spanned = spannable{ bold("some") + italic(" formatted") + color(Color.RED, " text") }
val nested = spannable{ bold(italic("nested ")) + url("www.google.com", "text") }
val noWrapping = bold("no ") + sub("wrapping ) + sup("also ") + "works"
text_view.text = spanned + nested + noWrapping
import android.text.Spannable
import android.text.SpannableString
import android.text.TextUtils
import android.text.style.*
fun spannable(func: () -> SpannableString) = func()
private fun span(s: CharSequence, o: Any) = (if (s is String) SpannableString(s) else s as? SpannableString
?: SpannableString("")).apply { setSpan(o, 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }
operator fun SpannableString.plus(s: SpannableString) = SpannableString(TextUtils.concat(this, s))
operator fun SpannableString.plus(s: String) = SpannableString(TextUtils.concat(this, s))
fun bold(s: CharSequence) = span(s, StyleSpan(android.graphics.Typeface.BOLD))
fun italic(s: CharSequence) = span(s, StyleSpan(android.graphics.Typeface.ITALIC))
fun underline(s: CharSequence) = span(s, UnderlineSpan())
fun strike(s: CharSequence) = span(s, StrikethroughSpan())
fun sup(s: CharSequence) = span(s, SuperscriptSpan())
fun sub(s: CharSequence) = span(s, SubscriptSpan())
fun size(size: Float, s: CharSequence) = span(s, RelativeSizeSpan(size))
fun color(color: Int, s: CharSequence) = span(s, ForegroundColorSpan(color))
fun background(color: Int, s: CharSequence) = span(s, BackgroundColorSpan(color))
fun url(url: String, s: CharSequence) = span(s, URLSpan(url))
@radoyankov
Copy link
Author

radoyankov commented Jun 19, 2019

This is amazing ❤️

Glad you like it ❤️

@Scorpio93
Copy link

Scorpio93 commented Jun 28, 2019

Thank you so much for your great idea.
My solution with using Kotlin extensions.

private const val EMPTY_STRING = ""
private const val FIRST_SYMBOL = 0

fun spannable(func: () -> SpannableString) = func()

private fun span(s: CharSequence, o: Any) = getNewSpannableString(s).apply {
    setSpan(o, FIRST_SYMBOL, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}

private fun getNewSpannableString(charSequence: CharSequence): SpannableString{
    return if (charSequence is String){
        SpannableString(charSequence)
    }else{
        charSequence as? SpannableString ?: SpannableString(EMPTY_STRING)
    }
}

operator fun SpannableString.plus(s: CharSequence) = SpannableString(TextUtils.concat(this, "", s))

fun CharSequence.makeSpannableString() = span(this, Spanned.SPAN_COMPOSING)
fun CharSequence.makeBold() = span(this, StyleSpan(BOLD))
fun CharSequence.makeItalic() = span(this, StyleSpan(ITALIC))
fun CharSequence.makeUnderline() = span(this, UnderlineSpan())
fun CharSequence.makeStrike() = span(this, StrikethroughSpan())
fun CharSequence.makeSuperscript() = span(this, SuperscriptSpan())
fun CharSequence.makeSubscript() = span(this, SubscriptSpan())
fun CharSequence.makeAnotherSize(size : Float) = span(this, RelativeSizeSpan(size))
fun CharSequence.makeAnotherColor(color : Int) = span(this, ForegroundColorSpan(color))
fun CharSequence.makeAnotherBackground(color : Int) = span(this, BackgroundColorSpan(color))
fun CharSequence.makeUrl(url : String) = span(this, URLSpan(url))

Using example:

textView.text = spannable{ 
    "Example".makeSpannableString()
             .makeBold()
             .makeItalic()
             .makeUnderline()
}

@HxBreak
Copy link

HxBreak commented Nov 30, 2020

nice job

@gonztirado
Copy link

gonztirado commented Mar 26, 2021

Thanks for all you guys! I'm using @Scorpio93 implementation, very nice job!

I also added a two more extensions for ClickableSpan:

/** You will need set movementMethod = LinkMovementMethod.getInstance() in the TextView to allow clicking on the span */
fun CharSequence.clickable(listener: View.OnClickListener) : SpannableString {
    val clickSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            listener.onClick(widget)
        }
    }
    return span(this, clickSpan)
}
/** You will need set movementMethod = LinkMovementMethod.getInstance() in the TextView to allow clicking on the span */
fun CharSequence.clickableWithoutUnderline(listener: View.OnClickListener) : SpannableString {
    val clickSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            listener.onClick(widget)
        }
        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.isUnderlineText = false
        }
    }
    return span(this, clickSpan)
}

@luckerbai
Copy link

Nice job~Thanks for all you guys!

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