Java:获取FontMetrics实例的更友好方法

4

有没有更友好的方法来获取FontMetrics的实例?

FontMetrics fm = Graphics.getFontMetrics(Font);

我不喜欢这种方法,因为以下例子说明了原因:
如果你想在游戏中创建一个菜单,并且想要所有的菜单项都居中显示,你需要使用字体度量。但是,大多数情况下,菜单项是可点击的。所以我创建了一个矩形数组,所有的矩形都适合于菜单项周围,这样当鼠标被按下时,我可以简单地使用
for (int i = 0; i < rects.length; i++)
if (rects[i].contains(mouseX, mouseY)) { ... }

但是为了创建这些矩形,我还需要它们的坐标的字体度量。这意味着我必须在菜单的绘制方法中构造所有的矩形。

因此,我希望有一种方法来获取字体度量,以便我可以在构造函数调用的方法中构造矩形。

7个回答

11

对我来说最简单的方法是:

Font font = new Font("Helvetica",Font.PLAIN,12);
Canvas c = new Canvas();
FontMetrics fm = c.getFontMetrics(font);

优点:

  1. 如果您调用c.getGraphics(),它将返回null(因此没有图形对象)。
  2. 这个(画布)也可以在无头模式下使用。

现在您可以轻松地获取高度和宽度...


Canvas::getFontMetrics 只是调用 Toolkit.getDefaultToolkit().getFontMetrics()。因此,你只是在实例化大量垃圾。 - Martijn Courteaux
你是怎么得出这个结论的?Canvas.java 没有自己的 getFontMetrics 方法实现。它继承了 Component.java 中的方法。在那里调用了 sun.font.FontDesignMetrics.getMetrics(font); 方法。如果你检查那个类,根本没有 Toolkit.getDefaultToolkit() 的调用... - Lonzak
啊哈,有趣。 不同的实现。 我想我正在查看Component.java的grepcode:http://developer.classpath.org/doc/java/awt/Component-source.html#2200 它是一个“peer”事务的网络。 但是,如果“peer”不存在,则会转到相同的文件行782。 我还没有明确检查过我的系统是否真正具有这样的对等体。 但是,鉴于画布是无头的,我不希望看到对等体(因为没有与OS /窗口管理器的连接)。 这就是我得出这个结论的方式。 - Martijn Courteaux
是的,但你引用了一些非常古老的代码,来自2007年(还没有提到被Oracle收购Sun)。所以我猜那时应该是JDK 1.6版本。那个版本已经很久以前EOL了。你至少要考虑使用JDK 1.8或更高版本... - Lonzak

6
真正正确的答案是使用Toolkit。
Font font = new Font("Courier New", Font.PLAIN, 14);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);

4
不是的。自Java 1.2版本以来,Toolkit.getFontMetrics(Font font)的使用已被弃用。它已被Font方法getLineMetrics所取代。 - Lonzak
4
LineMetrics无法满足我的需求......我已经看了一百遍。我能否使用LineMetrics来获取字符串的宽度? - Martijn Courteaux
为什么它被弃用了? - djangofan
我认为这是因为它太精确了,没有考虑到在将此字体呈现到光栅化显示器上时的像素化处理过程。直到今天,我仍然认为废弃是一个错误。 - Martijn Courteaux

2
一旦背景组件(即菜单后面的任何内容)被渲染,它就有一个Graphics对象,您可以使用该对象获取给定字体的度量,仅一次。
您肯定不希望在paint方法中执行此操作,因为该方法应尽可能轻量级。我会将此代码挂在一个侦听器上,在组件首次渲染时调用该侦听器。它可以将生成的FontMetrics对象存储在某个地方,以便稍后在paint方法中绘制那些菜单项框。
与在最后一刻确定菜单图形的测量值(即在paint时)相比,创建一些表示菜单的组件可能是一个好主意。您可以将这些组件放置在Glass Pane 更多信息在此处上,以便它们浮动在其他所有内容上方,并且您还将获得额外的奖励,即这些组件都能够接受鼠标点击并在其上触发侦听器事件,由于它们仅捕获自己几何上的事件,因此您甚至不必弄清楚单击了菜单的哪个部分(如果有)。
在这里使用组件的另一个优点是,您可以完全避免调整字体度量的要求。有现成的菜单项,或者您可以只使用 JLabels,并且您可以指定它们的对齐方式,您可以使用 LayoutManager 将框的大小调整为最大标签的宽度等等。

没有理由不允许通过非java.awt.Component组件层次结构传递Graphics/Component.getFontMetrics - Tom Hawtin - tackline
当然没错!我不否认这一点。我只是指出有一种“通过计算和绘制来完成所有图形”的方法,以及一种“将一些组件放在你的GUI上并让它们自行计算”的方法。这两种方法都有其优点。 - Carl Smotricz

1

我认为这是一个很好的解决方案

private static HashMap<Font, FontMetrics> fontmetrics = new HashMap<Font, FontMetrics>();


public static FontMetrics getFontMetrics(Font font)
{
    if (fontmetrics.containsKey(font))
    {
        return fontmetrics.get(font);
    }
    FontMetrics fm = createFontMetrics(font);
    fontmetrics.put(font, fm);
    return fm;
}

private static FontMetrics createFontMetrics(Font font)
{
    BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
    Graphics g = bi.getGraphics();
    FontMetrics fm = g.getFontMetrics(font);
    g.dispose();
    bi = null;
    return fm;
}

静态地图之所以错误,有很多原因。(虽然十年前我曾经使用它来进行性能优化。) - Tom Hawtin - tackline
1
这对我在服务器端代码上起作用了。谢谢。 - djangofan

1
假设菜单文本固定不变,您可以使用alpha透明度预先绘制文本到一个BufferedImage中,然后进行计算。需要菜单文本时,只需绘制该图像即可。
您仍然需要执行一些偏移量计算来使图像居中(假设面板大小可以更改),但这些计算应该相对轻量级。

0
更新建议。 FontMetrics 已被弃用,请改用 LineMetrics
String text = "some string";
FontRenderContext frc = new FontRenderContext(font.getTransform(), true, true);
LineMetrics lm = font.getLineMetrics(text, frc);

然而,一些方法例如SwingUtilities.computeStringWidth需要一个FontMetrics实例。另一个选项是计算字符串的边界。

Rectangle2D bounds = font.getStringBounds(text, frc);

然后可以从边界获取宽度和高度。


0

继Lonzak所说的,这个怎么样:

public static FontMetrics getFontMetrics(Font font){
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gd = ge.getDefaultScreenDevice();
    GraphicsConfiguration config = gd.getDefaultConfiguration();

    Canvas c = new Canvas(config);
    return c.getFontMetrics(font);
}

你可以将 'config' 变量存储为静态变量,这样它就只会在包含其他与游戏/开发环境相关的字体信息的实用字体类中构建一次。我猜你也可以对 canvas 变量这样做。

我认为当我需要在服务器端生成图像时,这并没有意义。 - djangofan

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