Search code examples
androidkotlinandroid-jetpack-composerenderingdrawing

Jetpack Compose Painter draw text


In my Jetpack Compose Android app, I used to have a "letter icon" Drawable that basically just draws some text. Now I'm trying to migrate that Drawable to a Compose Painter, but I can't find a way to draw text in the Painter. There are the DrawScope.drawText() methods, but all of them require a TextMeasurer. Usually, one would call rememberTextMeasurer() to get one, but this doesn't seems to be possible in this case because onDraw() is not a composable function. So I tried to instantiate a TextMeasurer myself:

TextMeasurer(
    defaultFontFamilyResolver = TODO(),
    defaultDensity = drawContext.density,
    defaultLayoutDirection = layoutDirection,
    cacheSize = 8
)

Most of the constructor parameters are available in the DrawScope, but I failed to get an instance of FontFamliy.Resolver. FontFamliy.Resolver is an interface only implemented by the internal class FontFamilyResolverImpl and the only way to get an instance of it seems to be from the LocalFontFamilyResolver composition local, which is also not accessible from the painter.

As a workaround, I managed to draw the text using DrawScope.drawIntoCanvas() and Canvas::nativeInstance and then using Android SDK APIs, like android.graphics.canvas.drawText(), but I would prefer a Compose-only approach.

Am I missing something here or is it not possible to draw text in a Painter using the current Compose APIs?

Thanks.


Solution

  • Usually, one would call rememberTextMeasurer() to get one, but this doesn't seems to be possible in this case because onDraw() is not a composable function

    You can always pass one in:

    import androidx.compose.foundation.Image
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.geometry.Size
    import androidx.compose.ui.graphics.drawscope.DrawScope
    import androidx.compose.ui.graphics.painter.Painter
    import androidx.compose.ui.text.TextMeasurer
    import androidx.compose.ui.text.rememberTextMeasurer
    
    public class TextPainter(public val textMeasurer: TextMeasurer) : Painter() {
        override val intrinsicSize: Size = Size.Unspecified
    
        override fun DrawScope.onDraw() {
            TODO("something with the TextMeasurer")
        }
    }
    
    @Composable
    internal fun PainterTextTest() {
        val textMeasurer = rememberTextMeasurer()
        
        Image(
            TextPainter(textMeasurer),
            "this is some content"
        )
    }
    

    Now I'm trying to migrate that Drawable to a Compose Painter

    I am skeptical that this is the best solution for whatever your overall problem is, but it should be doable.