tags:

views:

181

answers:

2

I'm trying to render a custom Swing component where I've extended the JComponent class.

For the purpose of simplifying the component requirements, lets just summarize my component as needing to render a few strings, each with their own fonts.

I need my component to be sized exactly to the summed width and height of my rendered strings.

In order to determine this size, I use the FontMetrics to calculate the dimensions of each string. Having this information I can figure out what size my component will be and resize it appropriately.

The problem is that when I access the getGraphics() it is always null, so I can't get the FontMetrics instance. If I wait to calculate my component size from the overriden paintComponent() method, its pretty much too late (the component already has a size, right?).

The documentation says that "This method will return null if this component is currently not displayable". So when do I know when the component is ready to be displayed and has a Graphics object for me to resize my component?

What is the Swing invokation order for rendering the component once the frame setVisible(true) is called?

Thanks


Update: Tuesday, Feb 06, 2010 at 23:34

As mentioned bellow in the comments, the GridLayout doesn't respect any setXxxSize() at all. For anyone interested, I've posted results of using the GridLayout, BoxLayout and FlowLayout using a simple frame that receives 5 fixed size components of 200 wide by 50 in height (by setting min, max and preferred).

Test Results:

The GridLayout is always resized along the width and height (as mentioned in comments)

The FlowLayout always respected the components size regardless.

As for the the BoxLayout...

The PAGE_AXIS and Y_AXIS shrank the width of the components to about half their size (104) but did not shrink the height.

The LINE_AXIS and X_AXIS shrank the height of the components to what seemed zero but did not touch the width.

+4  A: 

First, you can measure your Strings using the TextLayout class and its associated stuff. For example

public static void main(final String[] args) throws Exception
{
    final FontRenderContext frc = new FontRenderContext(null, true, true);
    final Font font = new Font("serif", Font.PLAIN, 18);
    final TextLayout layout = new TextLayout("This is a test", font, frc);
    final Rectangle2D bounds = layout.getBounds();
    System.err.println((int) (bounds.getWidth() + .5));
}

Secondly, you can be informed when your component has become visible by using a ComponentListener, but that's not necessary for measuring your strings in "advance"!

Jonathan Feinberg
I see that your creating your font? When I tried getFont(), the font is null. I'm assuming that a JComponent does not have a default font?
Jeach
A Component whose font is not explicitly set uses its parent's font. If you've got no parent, then you've got no font. You'd be better off explicitly setting your own font in your constructor.
Jonathan Feinberg
Thanks Jonathan! Setting the font is what I started to do. I can render the component correctly now this way, but for some reason this component still refuses to get sized to my dimensions. If I create a frame with a grid layout (0 rows, 1 col) and add 10 of my components, then make the frame full screen. Each item gets 1/10th the height of the screen in height when they shouldn't be much smaller. I set the min, max, preferred, setSize() and it still somehow renders to its own liking!!
Jeach
GridLayout does not respect the size constraints. You'll have to use BoxLayout, FlowLayout, or BorderLayout, in some combination.
Jonathan Feinberg
+1  A: 

Use a JLabel for rendering your Strings then you can just use the getPreferredSize() method of thel label.

camickr
I guess I expected someone to tell me to use existing Swing components to do this. My requirements are to remove much of the existing bloat from this component and render it directly myself. It use to be labels and icons within panels and within more panels with layouts, etc. I guess it was created with an automated tool. Now it must go on a diet I'm told!
Jeach
On second thought, event JLabel's are problematic because as mentioned in my comment above to Jonathan, the label should stay to a fixed height not much larger than the font size. JLabel's have a tendency to do exactly what my component is doing... stretch!
Jeach
As mentioned by Jonathan this is a layout problem, not a "preferred size" calculation. If you still want to use a GridLayout for your panel then another approach is to create a parent panel with a BorderLayout. Add you panel with the GridLayout to the NORTH of the parent panel. Now all the extra space will be given to the center and your panel will display at its preferred size.
camickr
Well the GridLayout is kind of useless if it doesn't respect requested sizes. But yet it's so convenient in its simple use. I have no time to implement an appropriate GridLayout (at this time), so I may just go with your last comment about using a GridLayout into the the NORTH part of a BorderLayout. Although I've been doing this for years now, I can't feel like there is something wrong with this... to me its bloat-ware! But I don't have much choice! Thanks camickr!
Jeach