在某个指定的矩形中绘制适合的文本

4
使用画布,我想要绘制一些短的标签文本(1-2个字符),使其适合某个指定的矩形。由于其他原因,我使用的缩放比例是这样的,即这个矩形的尺寸很小,大约为1。
我面临的问题是,在绘制文本之前(我使用Canva.drawText()进行绘制),计算最优的(尽可能大以使文本仍然适合)文本大小,用Paint.setTextSize设置文本大小。为此,我可以使用Paint.Fontmetrics对象获取一些通用的字体尺寸作为浮点数,或者使用getTextBounds(String text, int start, int end, Rect bounds)获取文本的边界框作为整数矩形。由于我使用的缩放比例,后者得到的整数边界框不够精确,无法计算出我的目的所需的最佳文本大小。
我需要的是一些方法,以更高的精确度获取文本的边界框(就像java.awt.FontMetrics中的getStringBounds(String str, Graphics context)一样),但我找不到合适的方法。
2个回答

8

我现在也忙于这个问题。

不幸的是,我尚未成功安装本地源,但我非常确定文本测量(包括getTextBounds()和measureText())与实际文本输出存在很大差异,特别是对于小字号(如Moritz的截图中所见)。 我认为测量方法使用字符的浮点宽度,而实际文本输出使用整数宽度(可能是出于性能考虑)。如果您需要绝对精确的大小(例如自动缩放),这当然会失败。 我用等宽字体进行了一些实验。这张图片展示了其中一些实验:

Android Autoscale experiments

这些实验中的文本比例是通过自动缩放设置的。

在顶行,我创建了一个整数递增的框。它们与android文本输出匹配。您可以看到,最后一个数字与屏幕不匹配!

在第二行,框被创建为浮点递增。框更好地匹配屏幕,但它们不匹配android文本输出!

在最后一行,框通过浮点递增增加,并且文本按相同的递增输出。框和字符都非常完美。

我的结论是,目前无法将Android文本输出设置为精确的宽度。问题似乎不在于测量方法,它们足够准确。问题似乎在于,您无法通过setTextSize()设置精确的文本宽度。

尝试以下步骤以重现此问题:

    float width = 100; // define a width which should be achieved
    m_textPaint.setTextSize( 100 ); // set a text size surely big enough
    Rect r = new Rect();
    String s = new String("This is a test text");
    m_textPaint.getTextBounds( s, 0, s.length(), r ); // measure the text with a random size
    float fac = width / r.width(); // compute the factor, which will scale the text to our target width
    Log.i( "MyTestOutput", "current text width:" + r.width() );
    Log.i( "MyTestOutput", "wanted text width:" + width );
    Log.i( "MyTestOutput", "factor:" + fac );
    Log.i( "MyTestOutput", "expected result:" + (float) r.width() * fac );
    m_textPaint.setTextSize( m_textPaint.getTextSize() * fac );
    m_textPaint.getTextBounds( s, 0, s.length(), r ); // now final measurement: whats the real width?
    Log.i( "MyTestOutput", "real result:" + r.width() );

我收到的输出如下:
05-26 12:18:18.420: I/MyTestOutput(23607): 当前文本宽度:1125 05-26 12:18:18.425: I/MyTestOutput(23607): 期望文本宽度:100.0 05-26 12:18:18.425: I/MyTestOutput(23607): 比例因子:0.08888889 05-26 12:18:18.425: I/MyTestOutput(23607): 预期结果:100.0 05-26 12:18:18.430: I/MyTestOutput(23607): 实际结果:94
所以我认为,实现非常准确的自动缩放文本的唯一方法是: a) 通过测量和设置文本大小来自动缩放它 b) 通过自己计算的增量逐个字符输出文本。
不幸的是,这仅适用于等宽字体。对于其他字体,这将更加复杂,但也是可能的。

1

我几天前刚做了这件事:请看我的博客

你可以使用 StaticLayout

// bmp1 is a source bitmap (in that case 44x44px big).
// density is the screen density, you will need to retrieve it yourself as well.

canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.overlay_44x44), new Matrix(), null);

// Initialize using a simple Paint object.
final TextPaint tp = new TextPaint(WHITE);

// Save the canvas state, it might be re-used later.
canvas.save();

// Create a padding to the left & top.
canvas.translate(4*density, 4*density);

// Clip the bitmap now.
canvas.clipRect(new Rect(0, 0, bmp1.getWidth(),(int) (bmp1.getHeight() - (6*density))));

// Basic StaticLayout with mostly default values
StaticLayout sl = new StaticLayout(message, tp, bmp1.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
sl.draw(canvas);

// Restore canvas.
canvas.restore();

抱歉,我发现我的原始问题不是很明确。问题不在于文本布局(文本只有1-2个字符长),而在于计算最佳文本大小。 - Christian Gawron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接