Java: Friendlier way to get an instance of FontMetrics

ε祈祈猫儿з 提交于 2019-11-29 08:02:18

For me the easiest way was to:

Font font = new Font("Helvetica",Font.PLAIN,12);
Canvas c = new Canvas();
FontMetrics fm = c.getFontMetrics(font);

Benefits:

  1. If you call c.getGraphics() it will return null (thus there is no graphics object)
  2. This (canvas) will also work in headless mode.

Now you can easily get height and width...

The really correct answer is to use Toolkit.

Font font = new Font("Courier New", Font.PLAIN, 14);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);

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.

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.

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

Adding to what Lonzak said, how about this:

public static FontMetrics getFontMetrics(Font font){
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gd = ge.getDefaultScreenDevice();
    GraphicsConfiguration config = gd.getDefaultConfiguration();

    Canvas c = new Canvas(config);
    return c.getFontMetrics(font);
}

You could store the 'config' variable as a static variable so it is constructed once in some utility font class that contains other font related information for your game/development environment. I guess you could also do this with the canvas variable.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!