在java中获取字符串大小(没有可用的图形对象)

时间:2021-08-31 12:06:08

I'm trying to write application which need to draw many strings using Graphics2D class in Java. I need to get sizes of each String object (to calculate exact position of each string). There is so many strings that it should be done before the paint() method is called and only once at the beginning of my program (so then I don't have Graphics2D object yet). I know that there is a method Font.getStringBounds() but it needs a FontRenderContext object as a parameter.

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

When i tried to create my own object:

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

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

and then obtain the strings bounds I've always get different sizes than when I obtain FontRenderContext using Graphics2D.getFontRenderContext() method inside paint() method. The differences are not big (about 1E-3) but I wonder why there is any difference?

然后获取字符串边界,我总是得到不同的大小,而不是使用Graphics2D.getFontRenderContext()方法在paint()方法中获取。差异并不大(大约1 -3),但我想知道为什么会有差异?

However, is there any better and secure way to obtain sizes of a string?

但是,是否有更好和安全的方法来获得字符串的大小?

Thnx for any help in advance!

谢谢你的帮助!

6 个解决方案

#1


6  

Try with the FontMetrics class; the stringWidth method returns the size of a string. An example:

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

JComponent c = getSomeKindOfJComponent();
FontMetrics fm = c.getFontMetrics(c.getFont()); // or another font
int strw = fm.stringWidth("My text");

#2


2  

You might also want to check out SwingUtilities.computeStringWidth.

您可能还想检查一下swinguse . computestringwidth。

#3


2  

Nev-ah. Gon-na. Happen.

Nev-ah。Gon-na。发生。

The reason is the rendering and computation you're looking for from FRC is specific to a Graphics context, i.e. a specific Graphics2D object. The one you're interested in is one you're handed at runtime- it's like no other (you have to assume).

原因是您从FRC中寻找的呈现和计算是特定于图形上下文的,即特定的Graphics2D对象。你感兴趣的那个是你在运行时交的,就像其他的一样(你必须假设)。

You can compute as much as you want using an FRC from some other Graphics2D, but your computations all all for naught when you try to use them at runtime with the Graphics2D paintComponent is handed, which is the Graphics2D you're going to use, no matter what.

你可以从其他的Graphics2D中使用FRC来计算,但是当你尝试在运行时使用Graphics2D paintComponent时,你的计算都是无用的,这是你将要使用的Graphics2D,无论如何。

So, yes, this would be nice but it's entirely theoretical. All that nice information is effectively locked away inside that FRC because without the exact Graphics2D the AttributedString is actually going to be drawn to, that FRC is worse than useless- it's an illusion you might actually try to embrace.

是的,这很好,但这完全是理论性的。所有这些好的信息都被有效地锁在了FRC内部,因为如果没有确切的Graphics2D, AttributedString会被吸引到,FRC比没用更糟糕——这是一个你可能会尝试去接受的错觉。

It makes sense, since everything really IS dependent on the Graphics2D you get handed at runtime. So the best thing to do is just accept it and write your code to call out from within paintComponent out to whatever objects and whatever specialized computation you have to do and build your design around the fact that THIS is the way things are.

这是有意义的,因为所有东西都依赖于在运行时交付的Graphics2D。所以最好的办法就是接受它并把你的代码写出来从paintComponent中调用到任何对象以及你需要做的任何专业计算,并围绕这个事实来构建你的设计。

I's a good question and a good thing to wish you could do, just, you can't. You see other people asking for this elsewhere on the web, in other forums. Notice the lack of useful answers and / or deafening silence.

我是个好问题,希望你能做,只是,你做不到。你可以在其他论坛上看到其他人在网上寻求这个。注意没有有用的答案和/或震耳欲聋的沉默。

#4


0  

Here is a snippet of code that does something similar -- wrote it for abbreviating the string to a given number of pixels.

这里有一个代码片段,它做了类似的事情——将字符串缩写成给定的像素。

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);
}

#5


0  

Besides using FontMetrics, a JLabel can be used to determine the size of both unformatted and (basic HTML) rendered text. Here is an example.

除了使用FontMetrics, JLabel还可以用来确定未格式化的和(基本的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();
  }
}

Edit 1: As to your "many strings" comment. Paint the strings to a BufferedImage that is only regenerated if needed. Use the BufferedImage each time paintComponent() is called.

编辑1:关于你的“许多字符串”评论。将字符串绘制到仅在需要时重新生成的BufferedImage。使用BufferedImage每次调用paintComponent()。

#6


0  

for historical sake,here's how I think he did it originally (jruby java pseucodoe)

出于历史原因,我认为他最初是这么做的(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

#1


6  

Try with the FontMetrics class; the stringWidth method returns the size of a string. An example:

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

JComponent c = getSomeKindOfJComponent();
FontMetrics fm = c.getFontMetrics(c.getFont()); // or another font
int strw = fm.stringWidth("My text");

#2


2  

You might also want to check out SwingUtilities.computeStringWidth.

您可能还想检查一下swinguse . computestringwidth。

#3


2  

Nev-ah. Gon-na. Happen.

Nev-ah。Gon-na。发生。

The reason is the rendering and computation you're looking for from FRC is specific to a Graphics context, i.e. a specific Graphics2D object. The one you're interested in is one you're handed at runtime- it's like no other (you have to assume).

原因是您从FRC中寻找的呈现和计算是特定于图形上下文的,即特定的Graphics2D对象。你感兴趣的那个是你在运行时交的,就像其他的一样(你必须假设)。

You can compute as much as you want using an FRC from some other Graphics2D, but your computations all all for naught when you try to use them at runtime with the Graphics2D paintComponent is handed, which is the Graphics2D you're going to use, no matter what.

你可以从其他的Graphics2D中使用FRC来计算,但是当你尝试在运行时使用Graphics2D paintComponent时,你的计算都是无用的,这是你将要使用的Graphics2D,无论如何。

So, yes, this would be nice but it's entirely theoretical. All that nice information is effectively locked away inside that FRC because without the exact Graphics2D the AttributedString is actually going to be drawn to, that FRC is worse than useless- it's an illusion you might actually try to embrace.

是的,这很好,但这完全是理论性的。所有这些好的信息都被有效地锁在了FRC内部,因为如果没有确切的Graphics2D, AttributedString会被吸引到,FRC比没用更糟糕——这是一个你可能会尝试去接受的错觉。

It makes sense, since everything really IS dependent on the Graphics2D you get handed at runtime. So the best thing to do is just accept it and write your code to call out from within paintComponent out to whatever objects and whatever specialized computation you have to do and build your design around the fact that THIS is the way things are.

这是有意义的,因为所有东西都依赖于在运行时交付的Graphics2D。所以最好的办法就是接受它并把你的代码写出来从paintComponent中调用到任何对象以及你需要做的任何专业计算,并围绕这个事实来构建你的设计。

I's a good question and a good thing to wish you could do, just, you can't. You see other people asking for this elsewhere on the web, in other forums. Notice the lack of useful answers and / or deafening silence.

我是个好问题,希望你能做,只是,你做不到。你可以在其他论坛上看到其他人在网上寻求这个。注意没有有用的答案和/或震耳欲聋的沉默。

#4


0  

Here is a snippet of code that does something similar -- wrote it for abbreviating the string to a given number of pixels.

这里有一个代码片段,它做了类似的事情——将字符串缩写成给定的像素。

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);
}

#5


0  

Besides using FontMetrics, a JLabel can be used to determine the size of both unformatted and (basic HTML) rendered text. Here is an example.

除了使用FontMetrics, JLabel还可以用来确定未格式化的和(基本的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();
  }
}

Edit 1: As to your "many strings" comment. Paint the strings to a BufferedImage that is only regenerated if needed. Use the BufferedImage each time paintComponent() is called.

编辑1:关于你的“许多字符串”评论。将字符串绘制到仅在需要时重新生成的BufferedImage。使用BufferedImage每次调用paintComponent()。

#6


0  

for historical sake,here's how I think he did it originally (jruby java pseucodoe)

出于历史原因,我认为他最初是这么做的(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