在Java中计算字符串的显示宽度

114

如何在Java中计算字符串的长度(以像素为单位)?

最好不要使用Swing。

编辑: 我想使用Java2D中的drawString()方法绘制字符串,并使用该长度进行自动换行。


5
没有摇摆?你在使用什么设备?用的是哪种字体?字号多大?是哪种风格?所有这些东西都会改变显示宽度。 - S.Lott
4
你会如何绘制字符串?使用AWT还是其他的工具包?字符串在像素上的大小取决于之后绘制像素的绘图API(当然也取决于你使用的字体以及字体大小和是否加粗/斜体等)。如果不知道绘图API和字体属性,那么字符串就没有任何大小。 - Mecki
1
@S. Lott。你说中了。我曾经有点想将其视为一个非问题而关闭。 - David Arno
对于.NET的等效类,有TextRenderer类,请参见https://dev59.com/YEbRa4cB1Zd3GeqP3L7e#604323。 - Spoike
5个回答

168
如果您只想使用AWT,请使用Graphics.getFontMetrics(可选择指定字体,以获取非默认字体)来获取FontMetrics,然后使用FontMetrics.stringWidth来查找指定字符串的宽度。
例如,如果您有一个名为gGraphics变量,则可以使用以下代码:
int width = g.getFontMetrics().stringWidth(text);

对于其他的工具包,您需要给我们更多信息 - 这总是取决于工具包。


10
如果您提供一个示例用法,可能会更清晰,最初我尝试将其用作静态方法,就像您写的那样。 - Aequitas
2
@Zich:我不确定你所说的哪种方法已被弃用 - 就我所知,在Java 8文档中,它们都没有被标记为已弃用... - Jon Skeet
3
@Zich:这是Graphics中的一种方法,而不是FontMetrics。但你正在调用已经被弃用的Toolkit.getFontMetrics,而且它与该方法所讨论的内容无关......你需要特别小心这种情况,特别是在开始报告错误之前...... - Jon Skeet
1
@Zich:嗯,我不会猜测 - 我会使用非弃用的方法,或者 Toolkit.getFontMetrics 建议的非弃用方法。 - Jon Skeet
对于SWT,您可以执行以下操作:http://ysgong.blogspot.de/2009/04/calculate-width-of-string-in-swt.html GC gc = new GC(drawable); Point size = gc.textExtent(string); gc.dispose ();然后,当字符串由drawable显示时,size.x是其宽度。 - Naxos84
显示剩余5条评论

69

并不总是需要依赖于工具包,也不总是需要使用FontMetrics方法,因为它要求先获得一个图形对象,但在网络容器或无界面环境中可能不存在。

我已经在Web Servlet中测试过这个方法,它可以计算文本的宽度。

import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;

...

String text = "Hello World";
AffineTransform affinetransform = new AffineTransform();     
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);     
Font font = new Font("Tahoma", Font.PLAIN, 12);
int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
int textheight = (int)(font.getStringBounds(text, frc).getHeight());
将必要的数值添加到这些尺寸中,以创建所需的边距。

1
我认为这种方式创建affineTransform和fontRenderContext不会产生良好的行为。我猜测font.getTransfrom()更加合理。 - Adrien Gorrell
2
你的示例仍在使用AWT功能。例如,使用SWT字体是行不通的,这就是为什么“它始终会依赖于工具包”的原因。 - serg.nechaev
1
@Olofu Mark - getStringBounds只提供逻辑边界。为了更好地使用它,应该使用getBounds()和LineMetrics对象来检索实际高度,包括ascent+descent。 - Jones
请注意:在使用Sun JDK的Docker容器中运行此程序可能会导致一系列问题,包括需要安装X11库、fontconfig和字体。即使完成了所有这些步骤,它仍然无法工作,因为它试图连接到不存在的显示器。 - Asad-ullah Khan

8

在以下类中使用getWidth方法:

import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;

class StringMetrics {

  Font font;
  FontRenderContext context;

  public StringMetrics(Graphics2D g2) {

    font = g2.getFont();
    context = g2.getFontRenderContext();
  }

  Rectangle2D getBounds(String message) {

    return font.getStringBounds(message, context);
  }

  double getWidth(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getWidth();
  }

  double getHeight(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getHeight();
  }

}

4

现在来点完全不同的东西。以下假设使用 Arial 字体,并基于字符与宽度的线性插值进行猜测。

// Returns the size in PICA of the string, given space is 200 and 'W' is 1000.
// see https://p2p.wrox.com/access/32197-calculate-character-widths.html

static int picaSize(String s)
{
    // the following characters are sorted by width in Arial font
    String lookup = " .:,;'^`!|jl/\\i-()JfIt[]?{}sr*a\"ce_gFzLxkP+0123456789<=>~qvy$SbduEphonTBCXY#VRKZN%GUAHD@OQ&wmMW";
    int result = 0;
    for (int i = 0; i < s.length(); ++i)
    {
        int c = lookup.indexOf(s.charAt(i));
        result += (c < 0 ? 60 : c) * 7 + 200;
    }
    return result;
}

有趣,但可能不太实用。

1
我个人正在寻找一种方法来计算多行字符串的面积,以便确定给定的区域是否足够大以打印该字符串 - 同时保留特定字体。
private static Hashtable hash = new Hashtable();
private Font font;
private LineBreakMeasurer lineBreakMeasurer;
private int start, end;

public PixelLengthCheck(Font font) {
    this.font = font;
}

public boolean tryIfStringFits(String textToMeasure, Dimension areaToFit) {
    AttributedString attributedString = new AttributedString(textToMeasure, hash);
    attributedString.addAttribute(TextAttribute.FONT, font);
    AttributedCharacterIterator attributedCharacterIterator =
            attributedString.getIterator();
    start = attributedCharacterIterator.getBeginIndex();
    end = attributedCharacterIterator.getEndIndex();

    lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator,
            new FontRenderContext(null, false, false));

    float width = (float) areaToFit.width;
    float height = 0;
    lineBreakMeasurer.setPosition(start);

    while (lineBreakMeasurer.getPosition() < end) {
        TextLayout textLayout = lineBreakMeasurer.nextLayout(width);
        height += textLayout.getAscent();
        height += textLayout.getDescent() + textLayout.getLeading();
    }

    boolean res = height <= areaToFit.getHeight();

    return res;
}

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