12

我正在尝试编写需要使用Graphics2DJava 中的类绘制许多字符串的应用程序。我需要获取每个 String 对象的大小(以计算每个字符串的确切位置)。有太多的字符串应该在paint()调用方法之前完成,并且只在我的程序开始时完成一次(所以我还没有Graphics2D对象)。我知道有一个方法Font.getStringBounds(),但它需要一个FontRenderContext对象作为参数。

当我尝试创建自己的对象时:

FontRenderContext frc = new FontRenderContext(MyFont.getTransform(), true, true)

FontRenderContext然后获得与使用Graphics2D.getFontRenderContext()方法 inside获得时相比,我总是得到不同大小的字符串边界paint()。差异不大(大约 1E-3),但我想知道为什么会有任何差异?

但是,有没有更好更安全的方法来获取字符串的大小?

感谢您提前提供任何帮助!

4

6 回答 6

5

尝试使用FontMetrics类;stringWidth方法返回字符串的大小。一个例子:

JComponent c = getSomeKindOfJComponent();
FontMetrics fm = c.getFontMetrics(c.getFont()); // or another font
int strw = fm.stringWidth("My text");
于 2011-02-06T15:38:27.977 回答
3

内华达。去吧。发生。

原因是您从 FRC 中寻找的渲染和计算特定于 Graphics 上下文,即特定的 Graphics2D 对象。您感兴趣的是您在运行时收到的一个 - 它与众不同(您必须假设)。

您可以使用来自其他一些Graphics2D的 FRC 进行尽可能多的计算,但是当您尝试在运行时使用它们时,您的计算都是徒劳的有什么关系。

所以,是的,这会很好,但这完全是理论上的。所有这些好的信息都被有效地锁定在 FRC 中,因为如果没有确切的 Graphics2D,AttributedString 将被实际绘制到,FRC 比无用更糟糕——这是你可能真正尝试接受的一种错觉。

这是有道理的,因为一切都依赖于您在运行时获得的 Graphics2D。因此,最好的办法就是接受它并编写代码,以便从paintComponent 中调用任何对象和必须执行的任何专门计算,并围绕事情就是这样的事实构建您的设计。

我是个好问题,也是一件好事,希望你能做到,只是,你不能。你看到其他人在网络上的其他地方,在其他论坛上要求这个。注意缺乏有用的答案和/或震耳欲聋的沉默。

于 2014-06-14T02:40:38.733 回答
2

您可能还想检查一下SwingUtilities.computeStringWidth

于 2011-02-06T16:54:34.833 回答
1

除了使用FontMetrics, aJLabel还可用于确定无格式文本和(基本 HTML)呈现文本的大小。这是一个例子。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import java.awt.image.BufferedImage;

import javax.swing.JOptionPane;
import javax.swing.JLabel;
import javax.swing.ImageIcon;

/** Sample code obtained from a thread on the Oracle forums that I cannot
locate at this instant.  My question was related to an unexpected rendering of
JLabel.  It was resolved by the 'added this' line courtesy of Darryl Burke. */
public class LabelRenderTest {

  String title = "<html><body style='width: 160px; padding: 8px'>"
          + "<h1>Do U C Me?</h1>"
          + "Here is a long string that will wrap.  "
          + "The effect we want is a multi-line label.";

  LabelRenderTest() {
    BufferedImage image = new BufferedImage(
            640,
            480,
            BufferedImage.TYPE_INT_RGB);
    Graphics2D imageGraphics = image.createGraphics();
    GradientPaint gp = new GradientPaint(
            20f, 20f, Color.blue,
            620f, 460f, Color.white);
    imageGraphics.setPaint(gp);
    imageGraphics.fillRect(0, 0, 800, 600);

    JLabel textLabel = new JLabel(title);
    textLabel.setSize(textLabel.getPreferredSize()); // <==== added this

    Dimension d = textLabel.getPreferredSize();
    BufferedImage bi = new BufferedImage(
            d.width,
            d.height,
            BufferedImage.TYPE_INT_ARGB);
    Graphics g = bi.createGraphics();
    g.setColor(new Color(255, 255, 255, 128));
    g.fillRoundRect(
            0,
            0,
            bi.getWidth(null),
            bi.getHeight(null),
            15,
            10);
    g.setColor(Color.black);
    textLabel.paint(g);
    Graphics g2 = image.getGraphics();
    g2.drawImage(bi, 20, 20, null);

    ImageIcon ii = new ImageIcon(image);
    JLabel imageLabel = new JLabel(ii);

    JOptionPane.showMessageDialog(null, imageLabel);
  }

  public static void main(String[] args) {
    LabelRenderTest ist = new LabelRenderTest();
  }
}

编辑1:至于您的“许多字符串”评论。将字符串绘制BufferedImage为仅在需要时才重新生成的字符串。每次paintComponent()调用时使用缓冲图像。

于 2011-02-06T16:24:24.613 回答
0

出于历史原因,这就是我认为他最初是如何做到的(jruby java pseucodoe)

font = UIManager.getFont("Label.font")
frc = java.awt.font.FontRenderContext.new(font.transform, true, true)
textLayout = java.awt.font.TextLayout.new(text, font, frc)
textLayout.bounds.width
于 2012-05-21T23:52:40.060 回答
0

这是一段代码,它做类似的事情——写它是为了将字符串缩写为给定数量的像素。

public static String abbreviate(final Graphics2D g2, final String text, final int fitToWidth) {
     // define how many characters in the caption can be drawn
     final FontMetrics fm = g2.getFontMetrics();
     Rectangle2D textBounds = fm.getStringBounds(text, g2);
     int count = text.length();
     while ((textBounds.getWidth() > fitToWidth) && (count > 4)) {
         textBounds = fm.getStringBounds(text.substring(0, count--), g2);
     }
     return count == text.length() ? text : StringUtils.abbreviate(text, count);
}
于 2011-02-06T15:38:59.903 回答