Получение размера строки в Java (без наличия графического объекта) - PullRequest
10 голосов
/ 06 февраля 2011

Я пытаюсь написать приложение, которое должно рисовать много строк, используя класс Graphics2D в Java. Мне нужно получить размеры каждого объекта String (чтобы вычислить точное положение каждой строки). Строк так много, что это нужно сделать до вызова метода paint () и только один раз в начале моей программы (поэтому у меня пока нет объекта Graphics2D). Я знаю, что есть метод Font.getStringBounds (), но ему нужен объект FontRenderContext в качестве параметра.

Когда я пытался создать свой собственный объект:

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

и затем получить границы строк. Я всегда получаю другие размеры, чем когда я получаю FontRenderContext, используя метод Graphics2D.getFontRenderContext () внутри метода paint (). Различия не большие (около 1E-3), но мне интересно, почему есть какая-либо разница?

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

Спасибо за любую помощь заранее!

Ответы [ 6 ]

6 голосов
/ 06 февраля 2011

Попробуйте использовать класс FontMetrics ; метод stringWidth возвращает размер строки. Пример:

JComponent c = getSomeKindOfJComponent();
FontMetrics fm = c.getFontMetrics(c.getFont()); // or another font
int strw = fm.stringWidth("My text");
2 голосов
/ 14 июня 2014

Нев-ах.Гон-на.Случаются.

Причина в том, что рендеринг и вычисления, которые вы ищете из FRC, специфичны для контекста Graphics, то есть для конкретного объекта Graphics2D.То, что вас интересует, это то, что вам вручают во время выполнения - это не похоже ни на что другое (вы должны предположить).

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

Так что, да, это было бы неплохо, но это абсолютно теоретически.Вся эта приятная информация эффективно блокируется внутри этого FRC, потому что без точного Graphics2D AttributedString фактически будет извлечена, этот FRC хуже, чем бесполезен - это иллюзия, которую вы могли бы попытаться охватить.

Это имеет смысл, поскольку все действительно зависит от Graphics2D, который вы получаете во время выполнения.Поэтому лучшее, что можно сделать, это просто принять его и написать свой код для вызова изнутри paintComponent к любым объектам и любым специализированным вычислениям, которые вам нужно сделать, и построить свой дизайн на основе того факта, что ЭТО так, как есть.

У меня хороший вопрос, и я хотел бы, чтобы вы могли это сделать, просто вы не можете.Вы видите других людей, просящих об этом в других местах в Интернете, на других форумах.Обратите внимание на отсутствие полезных ответов и / или оглушительное молчание.

2 голосов
/ 06 февраля 2011

Вы также можете проверить SwingUtilities.computeStringWidth.

1 голос
/ 06 февраля 2011

Помимо использования 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();
  }
}

Редактировать 1: Что касается вашего комментария "много строк". Нарисуйте строки в BufferedImage, который восстанавливается только при необходимости. Используйте буферизованное изображение каждый раз, когда вызывается paintComponent().

0 голосов
/ 22 мая 2012

ради истории, вот как я думаю, он сделал это изначально (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
0 голосов
/ 06 февраля 2011

Вот фрагмент кода, который делает нечто похожее - он написан для сокращения строки до заданного числа пикселей.

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