如何在JAVA中通过FontMetrics获取正确的字符串宽度

6

我使用FontMetrics对象上的stringWidth("str")方法计算字符串的宽度。这个方法只给出了从基线的起始点到结束点的宽度,而不是整个字符串的宽度。

有什么想法可以计算整个字符串的宽度吗?

大多数文档都表示不能通过将字符串中每个字符的宽度相加来计算结果。

以下是我的代码:

BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
FontMetrics fm = g.getFontMetrics(new Font("Arial", Font.PLAIN, 6));
int width = fm.stringWidth("Product name");

祝你好运!我曾经在这上面浪费了三天时间,尝试了14种技术,最终成功率大约为95%...自那以后,我尽量避免使用Swing。 - iluxa
@iluxa 嗯。谢谢 :-).. 对我来说95%是可以的。你能告诉我你做了什么吗? - Jakob Mathiasen
你对宽度做了什么?还有其他方法可以尝试使用。例如,可以使用getStringBounds。您还可以尝试使用GlyphVector而不是绘制字符串(我认为您最终会这样做),它是一种形状表示,并提供了许多其他选项。 - Radiodef
个人建议尝试使用GlyphVector。它可以做的事情比FontMetrics和drawString多得多:http://docs.oracle.com/javase/7/docs/api/java/awt/font/GlyphVector.html - Radiodef
2
兄弟,我希望我还记得...尝试使用SwingUtilities.computeStringWidth()。尝试这个技巧:https://dev59.com/qnVC5IYBdhLWcg3wixw0。希望对你有所帮助... - iluxa
3个回答

11

FontMetrics也有getStringBounds()方法,不仅仅是stringWidth()。

你应该问问自己需要文本宽度干什么。如果是为了通过paintComponent()重写输出,那么你应该在那里测量文本尺寸,这可以确保考虑到所有因素(例如fractionalmetrics、抗锯齿)。此外,您无需考虑释放图形上下文 - 在您的示例中,您肯定需要g.dispose()!

以下示例可在paintComponent()重写中使用,例如对于作为ContentPane的JPanel,它以您指定的字体在组件中心绘制文本,并在其周围绘制一个带有一些距离的矩形,文本完全位于其中心。

然而,文本大小,特别是垂直方向上的大小不精确。更好的解决方案在下面。

不精确解决方案的截图:http://i.imgur.com/vetRjCK.png

更精确的解决方案的截图在下面:http://i.imgur.com/0A0EdCf.png

final int w = getWidth();
final int h = getHeight();

// CLEAR BACKGROUND
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, w, h);

// ACTIVATE ANTIALIASING AND FRACTIONAL METRICS
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

// PREPARE TEXT, COLOR, FONT
final String text = "The Higgs Boson is ...";
g.setColor(Color.ORANGE);
g.setFont(yourFont);

// PREPARE COORDINATES, AND DRAW TEXT
final FontMetrics fm = g.getFontMetrics();
final Rectangle2D stringBounds = fm.getStringBounds(text, g);
final double x = (w - stringBounds.getWidth()) / 2d;
final double y = (h - stringBounds.getHeight()) / 2d;
g.drawString(text, (int) x, (int) (y + fm.getAscent()));

// TURN OFF ANTIALIASING FOR HIGHER VISUAL PRECISION OF THE LINES
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

// DRAW RECTANGLE BORDER
final double borderDistance = 10d;
final Shape borderRect = new Rectangle2D.Double(x - borderDistance * 2, y - borderDistance, stringBounds.getWidth() + borderDistance * 4, stringBounds.getHeight() + borderDistance * 2);
g.setStroke(new BasicStroke(3f));
g.draw(borderRect);

// DRAW THIN TIGHT RECTANGLE BORDER
final Shape borderRectTight = new Rectangle2D.Double(x, y , stringBounds.getWidth(), stringBounds.getHeight());
g.setStroke(new BasicStroke(1f));
g.setColor(Color.GRAY);
g.draw(borderRectTight);
下面的解决方案结构与上面类似,但不是使用FontMetrics调用来大致推导文字尺寸,而是先将文本转换为形状,以得出精确的文本尺寸
// CLEAR BACKGROUND
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, w, h);

// ACTIVATE ANTIALIASING AND FRACTIONAL METRICS
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

// PREPARE TEXT, COLOR
final String text = "The Higgs Boson is ...";
g.setColor(Color.ORANGE);

// CREATE GLYPHVECTOR FROM TEXT, CREATE PRELIMINARY SHAPE FOR COORDINATE CALCULATION, CALC COORDINATES
final GlyphVector gv = yourFont.createGlyphVector(g.getFontRenderContext(), text);
final Rectangle2D stringBoundsForPosition = gv.getOutline().getBounds2D();
final double xForShapeCreation = (w - stringBoundsForPosition.getWidth()) / 2d;
final double yForShapeCreation = (h - stringBoundsForPosition.getHeight()) / 2d;

// DERIVE SHAPE AGAIN, THIS TIME IN THE RIGHT PLACE (IT'S NOT THE ONLY POSSIBLE METHOD.)
final Shape textShape = gv.getOutline((float) xForShapeCreation, (float) yForShapeCreation + g.getFontMetrics(yourFont).getAscent());
g.fill(textShape);

// GET PRECISE SHAPE BOUNDS, TURN OFF ANTIALIASING FOR HIGHER VISUAL PRECISION OF THE LINES
final Rectangle2D stringBoundsForEverything = textShape.getBounds2D();// JavaDocs: "Returns a high precision [...] bounding box of the Shape [...] guarantee [...] that the Shape lies entirely within the indicated Rectangle2D."
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

// DRAW RECTANGLE BORDER
final double borderDistance = 10d;
final Shape borderRect = new Rectangle2D.Double(stringBoundsForEverything.getX() - borderDistance * 2, stringBoundsForEverything.getY() - borderDistance, stringBoundsForEverything.getWidth() + borderDistance * 4, stringBoundsForEverything.getHeight() + borderDistance * 2);
g.setStroke(new BasicStroke(3f));
g.draw(borderRect);

// DRAW THIN TIGHT RECTANGLE BORDER
final Shape borderRectTight = new Rectangle2D.Double(stringBoundsForEverything.getX(), stringBoundsForEverything.getY(), stringBoundsForEverything.getWidth(), stringBoundsForEverything.getHeight());
g.setStroke(new BasicStroke(1f));
g.setColor(Color.GRAY);
g.draw(borderRectTight);

1
感谢您的评论,@Dreamspace总裁。但是,由于我在一年前提出了这个问题,现在系统已经在客户那里运行。解决方案并不完美,但我们可以接受它。 - Jakob Mathiasen

0
我在几年前构建了一个 XML 渲染组件,并且非常依赖于 SwingUtilities.computeStringWidth(fontMetrics, string)。我从要测量字符串的组件中获取字体度量。

myComponent.getFontMetrics(myComponent.getFont());


0

你需要一个图形对象。

graphics.setFont(font);

int length = graphics.getFontMetrics(graphics.getFont()).stringWidth(value);


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