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

Android Canvas绘制文本时如何正确计算基线y以适配Paint.getTextBounds边界

正确计算基线坐标的方案

你出现截断的核心原因是使用了全局字体的FontMetrics.descent参数计算基线,该参数是字体所有字符的最大下行高度,和当前传入文本的实际边界不匹配。而Paint.getTextBounds()返回的Rect是针对你当前输入文本计算的实际渲染边界,直接用它的参数计算即可:
Paint.getTextBounds()返回的Rect默认以基线为坐标原点:

  • rect.top:文本顶部相对基线的偏移(为负值,因为在基线上方)
  • rect.bottom:文本底部相对基线的偏移(为正值,因为在基线下方)
    要让文本刚好完整渲染在大小为rect.width() * rect.height()的Bitmap中,基线的y坐标直接取-rect.top即可,无需使用FontMetrics的全局参数。

修改后的核心绘制代码如下:

final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
final Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);

final int width = rect.width();
final int height = rect.height();

Bitmap rendered = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas gOfRendered = new Canvas(rendered);

paint.setColor(Color.WHITE);
// 核心修改:x偏移抵消rect.left的字体内边距,y坐标直接取-rect.top作为基线位置
gOfRendered.drawText(
        text, 
        -rect.left, 
        -rect.top, 
        paint
);

gOfRendered.drawColor(0x11ffffff); // 用于查看边界

这个方案对所有文本都生效,不管文本有没有下行字符(比如g、y这类带descent的字符,或者back这类无descent的字符),都不会出现截断问题,且文本会刚好填满getTextBounds返回的边界范围。


关于非基线参数的绘制接口

原生Android Canvas没有直接接收文本顶部坐标作为参数的单行文本绘制方法,你可以基于上面的基线计算逻辑封装一个工具方法,传入左上角坐标即可自动换算基线:

/**
 * 绘制单行文本,坐标为文本左上角位置
 * @param canvas 画布
 * @param text 待绘制文本
 * @param left 文本左上角x坐标
 * @param top 文本左上角y坐标
 * @param paint 绘制用Paint
 */
public void drawTextByTopLeft(Canvas canvas, String text, float left, float top, Paint paint) {
    Rect rect = new Rect();
    paint.getTextBounds(text, 0, text.length(), rect);
    canvas.drawText(text, left - rect.left, top - rect.top, paint);
}

如果需要绘制多行文本,可以直接使用StaticLayout,它默认从传入的左上角坐标开始绘制,不需要手动计算基线。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 09:24:01