Saturday, 15 October 2022

Add extra line on canvas with different textual properties

The code below draws multiline text on a canvas.

fun Canvas.drawMultilineText(
    text: CharSequence,
    textPaint: TextPaint,
    width: Int,
    x: Float,
    y: Float,
    start: Int = 0,
    end: Int = text.length,
    alignment: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL,
    spacingMult: Float = 1f,
    spacingAdd: Float = 0f,
    includePad: Boolean = true,
    ellipsizedWidth: Int = width,
    ellipsize: TextUtils.TruncateAt? = null) {

    val cacheKey = "$text-$start-$end-$textPaint-$width-$alignment-" +
            "$spacingMult-$spacingAdd-$includePad-$ellipsizedWidth-$ellipsize"

    val staticLayout = StaticLayoutCache[cacheKey] ?: StaticLayout.Builder.obtain(text, start, end, textPaint, width)
        .setAlignment(alignment)
        .setLineSpacing(spacingAdd, spacingMult)
        .setIncludePad(includePad)
        .setEllipsizedWidth(ellipsizedWidth)
        .setEllipsize(ellipsize)
        .build()

    staticLayout.draw(this, x, y)
}

private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) {
    canvas.withTranslation(x, y) {
        draw(this)
    }
}

private object StaticLayoutCache {

    private const val MAX_SIZE = 50 // Arbitrary max number of cached items
    private val cache = lruCache<String, StaticLayout>(MAX_SIZE)

    operator fun set(key: String, staticLayout: StaticLayout) {
        cache.put(key, staticLayout)
    }

    operator fun get(key: String): StaticLayout? {
        return cache[key]
    }
}

This is how I draw a piece of text:

val textPaint = TextPaint()
textPaint.textSize = 10f
canvas.drawMultilineText(myString, textPaint, canvas.width, 100f, 100f)

It works well. But, I did not find a way to add another piece of text underneath that with a different textPaint. Only solution I found is adding a new text with a different y position as below, but that's not elegant because there's overlapping if myString is too long. So, what should I do?

val textPaint2 = TextPaint()
textPaint2.textSize = 5f
canvas.drawMultilineText("This is text underneath myString", textPaint2, canvas.width, 100f, 200f)


from Add extra line on canvas with different textual properties

No comments:

Post a Comment