In this tutorial, you'll learn how to create a Kotlin function that automatically detects and colors Markdown-style bold text (surrounded by **) in an Android TextView. This is useful for formatting text dynamically without using HTML or multiple TextViews.

1. Problem Statement

Many apps need to format text dynamically, such as:

  • Highlighting titles or keywords in user-generated content
  • Applying custom styling to parts of a string
  • Supporting simple Markdown-like syntax in TextViews

Instead of using Spannable manually every time, we can create a reusable function that detects **text** and applies custom coloring.

2. Solution Overview

We’ll create a Kotlin extension function for TextView that:

  1. Finds all occurrences of **text** using regex
  2. Applies a custom color to the bold text
  3. Makes the ** markers invisible (transparent)

3. Implementation Steps

Step 1: Create the Kotlin Extension Function

Add this function to your project (e.g., in TextViewExtensions.kt):

import android.graphics.Color
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.widget.TextView
import androidx.annotation.ColorInt

/**
 * Applies a color to text wrapped in **double asterisks** (Markdown-style bold),
 * while making the asterisks transparent.
 *
 * @param color The color to apply to the bold text (e.g., ContextCompat.getColor(...))
 */
fun TextView.colorMarkdownBoldText(@ColorInt color: Int) {
    val text = this.text.toString()
    if (text.contains("**")) {
        val spannable = SpannableString(text)
        val regex = Regex("\\*\\*(.*?)\\*\\*") // Finds **text**

        // Apply color to each match
        regex.findAll(text).forEach { match ->
            val boldTextStart = match.range.first
            val boldTextEnd = match.range.last + 1 // +1 because range is inclusive

            // Color the text inside ** **
            spannable.setSpan(
                ForegroundColorSpan(color),
                boldTextStart,
                boldTextEnd,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            )

            // Hide the ** markers by making them transparent
            spannable.setSpan(
                ForegroundColorSpan(Color.TRANSPARENT),
                boldTextStart,
                boldTextStart + 2, // First two chars (**)
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            )
            spannable.setSpan(
                ForegroundColorSpan(Color.TRANSPARENT),
                boldTextEnd - 2,
                boldTextEnd, // Last two chars (**)
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
        this.text = spannable
    }
}

Step 2: Use the Function in Your Activity/Fragment

Call the function on any TextView:

val textView = findViewById<TextView>(R.id.textView)
textView.text = "**The Paradox of Choice**\nDid you know that having too many options..."

// Apply color (e.g., from resources)
textView.colorMarkdownBoldText(ContextCompat.getColor(this, R.color.purple_500))

4. Advanced Customization

Make It Support More Markdown (Optional)

You can extend the function to handle other Markdown syntax, such as:

  • *italic* → Apply Typeface.ITALIC
  • ~~strikethrough~~ → Apply StrikethroughSpan

Example:

// Inside the regex loop, add:
spannable.setSpan(
    StyleSpan(Typeface.BOLD), // Makes text bold
    boldTextStart + 2, // Skip **
    boldTextEnd - 2,   // Skip **
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)

5. Performance Considerations

  • Regex overhead: If the text is very long, consider running this in a background thread.
  • Reusability: This works best for short, dynamic text. For long Markdown documents, use a library like Markwon.

7. Conclusion

With this approach, you can:

✅ Dynamically style **bold** text in TextView
✅ Keep code clean with an extension function
✅ Extend it for other Markdown features

This is useful for chat apps, note-taking apps, or anywhere you need simple text formatting without HTML.