views:

102

answers:

3

Hi people,

Is there a friendlier way to get an instance of FontMetrics than

FontMetrics fm = Graphics.getFontMetrics(Font);

I hate this way because of the following example:

If you want to create in a game a menu and you want all the menuitems in the center of the screen you need fontmetrics. But, mostly, menuitems are clickable. So I create an array of Rectangles and all the rectangles fits around the items, so when the mouse is pressed, I can simply use

for (int i = 0; i < rects.length; i++)
if (rects[i].contains(mouseX, mouseY)) { ... }

But to create the rects I also need FontMetrics for their coordinates. So this mean that I have to construct all my rectangles in the paint-method of my menu.

So I want a way to get the FontMetrics so I can construct the Rectangles in a method called by the constructor.

Hope you understand.
Thanks in advance.

+2  A: 

Once the background component, i.e. whatever is behind your menu, has been rendered, it has a Graphics object that you can use to get the metrics for a given font, just once.

You certainly don't want to be doing this in the paint method, which should be as lightweight as possible. I'd hang this code on a listener that gets called when the component is first rendered. It can store the resulting FontMetrics object somewhere where you can later access it, either in a paint method for drawing those menu item boxes.

Rather than determining the measurements of your menu graphics at the last moment, i.e. when painting, it might be a good idea instead to create some components to represent your menu. You can place those components on the Glass Pane more info here so they'll float above everything else, and you'll have the added bonus that those components are all capable of accepting mouse clicks and firing listener events on them, and since they only capture events on their own geometry you don't even have to figure out which part of menu was hit by the click, if at all.

Another advantage of using components here is that you may entirely get around the requirement for fiddling with font metrics. There are ready-made menu items, or you could just use JLabels, and you can specify their alignment, you can use a LayoutManager to size the boxes to the width of the biggest label, and so forth.

Carl Smotricz
There is no reason why `Graphics`/`Component.getFontMetrics` can't be passed down through a non-`java.awt.Component` component hierarchy.
Tom Hawtin - tackline
Absolutely! And I'm not disputing that. I just pointed out that there is a school of "do all your graphics by computing and drawing" and one of "throw some components on your GUI and let them compute themselves." Both approaches have merits.
Carl Smotricz
A: 

Assuming the menu text is fixed, you could pre-draw the text to a BufferedImage with alpha transparency and make your calculations then. Then, when you need the menu text, just draw the image.

You'll still have to do some offset calculations to centre the image (assuming the panel size can change), but these should be relatively lightweight.

McDowell
A: 

I think this is a good solution

private static HashMap<Font, FontMetrics> fontmetrics = new HashMap<Font, FontMetrics>();


public static FontMetrics getFontMetrics(Font font)
{
    if (fontmetrics.containsKey(font))
    {
        return fontmetrics.get(font);
    }
    FontMetrics fm = createFontMetrics(font);
    fontmetrics.put(font, fm);
    return fm;
}

private static FontMetrics createFontMetrics(Font font)
{
    BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
    Graphics g = bi.getGraphics();
    FontMetrics fm = g.getFontMetrics(font);
    g.dispose();
    bi = null;
    return fm;
}
Martijn Courteaux
A static map is wrong for so many reasons. (Although a decade ago I did use it for a performance hack.)
Tom Hawtin - tackline