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:
- Finds all occurrences of
**text**using regex - Applies a custom color to the bold text
- 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*→ ApplyTypeface.ITALIC~~strikethrough~~→ ApplyStrikethroughSpan
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.