Как получить правильную ширину строки из FontMetrics в JAVA

Я рассчитываю ширину строки, используя stringWidth("str") метод на FontMetrics объект. Этот метод только дает мне ширину от начальной точки до конечной точки на базовой линии. Не общая ширина строки.

Любые идеи о том, как рассчитать общую ширину?

В большинстве документов говорится, что я не могу полагаться на результат, добавляя ширину каждого символа в строке. Вот мой код до сих пор:

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

3 ответа

FontMetrics также имеет getStringBounds(), а не только stringWidth().

Вы должны спросить себя, для чего вам нужна ширина текста. Если это для вывода с помощью, например, переопределения paintComponent(), то вы должны измерить размеры текста там, чтобы убедиться, что все факторы (например, дробная метрика, сглаживание) приняты во внимание. Кроме того, вам не нужно думать об удалении графического контекста - что в вашем примере вам определенно необходимо, для этого требуется g.dispose()!

В следующем примере для использования в переопределении paintComponent(), например, для JPanel, который вы используете в качестве ContentPane, рисует текст в центре компонента шрифтом, заданным вами, и рисует прямоугольник вокруг него на некотором расстоянии, текст идеально в центре.

Однако размер текста, особенно вертикальный, не является точным. Лучшее решение - дальше вниз.

Снимок экрана этого неточного решения: https://i.imgur.com/vetRjCK.png

Снимок экрана точного решения ниже: https://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);

Я создал компонент рендеринга xml несколько лет назад и очень сильно полагаюсь на

SwingUtilities.computeStringWidth(fontMetrics, string);

Я получаю метрики шрифта от компонента, для которого я измеряю строку.

myComponent.getFontMetrics(myComponent.getFont());

Вам нужен графический объект

graphics.setFont(font);

int lenghtgraphics.getFontMetrics(graphics.getFont()).stringWidth(value);

Другие вопросы по тегам