Following @Håvard answer and facing the same issue that @Vivek modi had, I implemented the next working solution which accounts for custom fonts + line spacing extras and is written in Kotlin:
https://gist.github.com/marcelpallares/ae6285ee0dcb3f04493991dcb18d01bd
class NumberIndentSpan(
private val number: Int,
private val leadWidth: Int = 15,
private val gapWidth: Int = 30,
) : LeadingMarginSpan {
override fun getLeadingMargin(first: Boolean): Int {
return leadWidth + gapWidth
}
override fun drawLeadingMargin(
canvas: Canvas,
paint: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
t: CharSequence?,
start: Int,
end: Int,
first: Boolean,
layout: Layout?
) {
if (first) {
val text = "$number."
val width = paint.measureText(text)
val yPosition = baseline.toFloat()
val xPosition = (leadWidth + x - width / 2) * dir
canvas.drawText(text, xPosition, yPosition, paint)
}
}
}
I also added an extension method to use it:
fun TextView.setNumberedText(list: List<String>) {
val content = SpannableStringBuilder()
val gap = context.resources.getDimensionPixelSize(R.dimen.margin_16)
list.forEachIndexed { index, text ->
val contentStart = content.length
content.appendLine(text)
content.setSpan(NumberIndentSpan(index + 1, gapWidth = gap), contentStart, content.length, 0)
}
text = content
}
And finally a custom TextView to be able to use this from XML layouts
class OrderedListTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {
init {
setNumberedText(text.lines())
}
}