How to get the correct String width from FontMetrics in JAVA

前端 未结 3 1941
南笙
南笙 2021-01-01 03:05

I calculate the width of a string by using the stringWidth(\"str\") method on the FontMetrics object. This method only gives me the width from the

相关标签:
3条回答
  • 2021-01-01 03:25

    I built an xml rendering component a few years back and rely very heavily on

    SwingUtilities.computeStringWidth(fontMetrics, string);

    I get the font metrics from the component that I'm measuring the string for.

    myComponent.getFontMetrics(myComponent.getFont());

    0 讨论(0)
  • 2021-01-01 03:39

    you need a graphics object.

    graphics.setFont(font);

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

    0 讨论(0)
  • 2021-01-01 03:41

    FontMetrics also has getStringBounds(), not just stringWidth().

    You should ask yourself what you need the text width for. If it's for output via e.g. a paintComponent() override, then you should measure the text dimensions there, which makes sure that all factors (e.g. fractionalmetrics, antialiasing) are taken into consideration. Also, you don't have to think about disposing the graphics context - which in your example, you definitely have to, it needs g.dispose()!

    The following example for use in a paintComponent() override, e.g. for the JPanel you're using as your ContentPane, draws a text at the center of the component in a font given by you and draws a rectangle around it with some distance, the text being perfectly in its center.

    The text size, especially vertically, is not precise, however. A better solution is further down.

    Screenshot of this imprecise solution: http://i.imgur.com/vetRjCK.png

    Screenshot of precise solution further down: http://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);
    

    The following solution is structurally just like the above, but instead of using FontMetrics-calls to loosely derive the text dimensions, it derives the precise text dimensions by converting the text into a Shape first.

    // 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);
    
    0 讨论(0)
提交回复
热议问题