You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose Canvas调用drawText文本超出屏幕,未遵循宽度限制

Compose Canvas绘制文本超出设置宽度的问题排查

我用以下代码在Compose Canvas上绘制文本,但截图显示文本超出屏幕范围,没有遵循给Canvas设置的500dp宽度参数。旧版Android Canvas不会有这个问题,这是Compose的Bug吗?

Box(
    modifier = Modifier
        .fillMaxWidth()
) {
    Canvas(
        modifier = Modifier.width(500.dp),
        onDraw = {
            drawIntoCanvas {
                it.nativeCanvas.drawText(
                    text,
                    0f,
                    120.dp.toPx(),
                    textPaintStroke(context = context)
                )
                it.nativeCanvas.drawText(
                    text,
                    0f,
                    120.dp.toPx(),
                    textPaint(context = context)
                )
            }
        }
    )
}

fun textPaintStroke(
    context: Context
): NativePaint {
    val customTypeface = ResourcesCompat.getFont(context, R.font.baloo2_semi_bold)

    return Paint().asFrameworkPaint().apply {
        isAntiAlias = true
        style = android.graphics.Paint.Style.STROKE
        textSize = 64f
        color = android.graphics.Color.BLACK
        strokeWidth = 12f
        strokeMiter= 10f
        strokeJoin = android.graphics.Paint.Join.ROUND
        typeface = customTypeface
    }
}

fun textPaint(
    context: Context
): NativePaint {
    val customTypeface = ResourcesCompat.getFont(context, R.font.baloo2_semi_bold)

    return Paint().asFrameworkPaint().apply {
        isAntiAlias = true
        style = android.graphics.Paint.Style.FILL
        textSize = 64f
        color = android.graphics.Color.WHITE
        typeface = customTypeface
    }
}

文本超出屏幕截图


问题原因

这不是Compose的Bug,核心原因是你直接调用nativeCanvas.drawText绕过了Compose Canvas的布局约束:

  • Compose Canvas的modifier.width(500.dp)仅用于确定自身的布局尺寸,不会自动限制原生Canvas的绘制范围。
  • 原生Canvas的绘制空间默认对应整个父布局的画布区域,不会自动裁剪超出modifier设定区域的内容,旧版Android自定义View的onDraw默认也不会自动裁剪,除非手动设置裁剪规则。

解决方法

方法1:手动添加裁剪区域

在drawIntoCanvas外层通过clipRect限制绘制范围,使用size.width获取Canvas设定宽度的像素值:

Canvas(
    modifier = Modifier.width(500.dp),
    onDraw = {
        // 裁剪到Canvas的实际布局尺寸
        clipRect(0f, 0f, size.width, size.height) {
            drawIntoCanvas {
                it.nativeCanvas.drawText(
                    text,
                    0f,
                    120.dp.toPx(),
                    textPaintStroke(context = context)
                )
                it.nativeCanvas.drawText(
                    text,
                    0f,
                    120.dp.toPx(),
                    textPaint(context = context)
                )
            }
        }
    }
)

方法2:使用Compose原生drawText API

改用Compose提供的drawText和TextMeasurer,更符合Compose设计范式,自动适配布局约束:

val textMeasurer = rememberTextMeasurer()
Canvas(modifier = Modifier.width(500.dp)) {
    // 绘制描边文本
    drawText(
        textMeasurer = textMeasurer,
        text = text,
        style = TextStyle(
            fontSize = 64.sp,
            fontFamily = FontFamily(Font(R.font.baloo2_semi_bold)),
            color = Color.Black,
            drawStyle = Stroke(width = 12f, miter = 10f, join = StrokeJoin.Round)
        ),
        topLeft = Offset(0f, 120.dp.toPx())
    )
    // 绘制填充文本
    drawText(
        textMeasurer = textMeasurer,
        text = text,
        style = TextStyle(
            fontSize = 64.sp,
            fontFamily = FontFamily(Font(R.font.baloo2_semi_bold)),
            color = Color.White
        ),
        topLeft = Offset(0f, 120.dp.toPx())
    )
}

如果需要文本自动换行或严格限制宽度,可以配合Constraints参数给TextMeasurer.measure设置最大宽度。


内容的提问来源于stack exchange,提问作者alfietap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 00:15:30