tags:

views:

73

answers:

3
public class MyFrame extends JFrame
{
public MyFrame(String title)
{

    setSize(200, 200);
    setTitle(Integer.toString(super.getSize().width));
    setLayout(new FlowLayout());
    for (int i = 0; i < 5; ++i)
    {
        JButton b = new JButton();
        b.setSize(90,50);
        b.setText(Integer.toString(b.getSize().width));
        this.add(b);![alt text][1]
    }
    this.setVisible(true);
}
}

why if having button widht 90 I'm getting window where three buttons are in one row instead of two?

+2  A: 

FlowLayout will lay out Components left-to-right (or right-to-left) wrapping them if required. If you wish to explicitly set the size of each JButton you should use setPreferredSize rather than setSize as layout managers typically make use of the minimum, preferred and maximum sizes when performing a layout.

Size properties are quite confusing - There is an interesting article here. In particular, note:

Are the size properties always honored?

Some layout managers, such as GridLayout, completely ignore the size properties.

FlowLayout, attempts to honor both dimensions of preferredSize, and possibly has no need to honor either minimumSize or maximumSize.

Adamski
But why 3? If frame is 200 I thought that there would be place just for two buttons?
There is nothing we can do
Sorry - I will amend my answer.
Adamski
So basiclally the answer is...?
There is nothing we can do
No, the answer is that FlowLayout will respect the preferredSize so you should use that. It is ignoring the call to setSize and simply sizing each JButton according to its preferredSize, which is being dictated by the text within the button (because you haven't set it explicitly).
Adamski
A: 

For one thing, you're not using JFrame correctly: you don't add components directly to the frame, you add them to a JPanel that you then pass to the frame with setContentPane().

Also: it's not very elegant to directly subclass JFrame just to add components. Instead, create your frame as a separate object.

Anon
This doesn't answer the OP's question. Also there's nothing wrong with writing frame.add(Component). From the JFrame doc: "As a conveniance add and its variants, remove and setLayout have been overridden to forward to the contentPane as necessary. This means you can write: frame.add(child);". Finally, I don't necessarily think it's bad practice to subclass JFrame for your top-level application frame (just as you would subclass JPanel). Otherwise you simply end up with a tonne of Swing code in your Main class.
Adamski
@Adamski - I wouldn't subclass JPanel either. For one thing, it violates the Liskov Substitution Principle. Nor is there any reason to put that code in your main class: your classes can manage their frames/panels, they just shouldn't subclass JFrame/JPanel.
Anon
Why does it violate the principle? JPanel is simply an empty component and hence if I subclass it as MyPanel and then substitute in the use of MyPanel is my application incorrect in any way? All JPanel methods will still behave as expected. By this token JFileChooser and JColorChooser have also violated the principle because they subclass JComponent.
Adamski
@Adamski - take your subclass and hand it off to some code that expects an empty panel. Is it substitutable?
Anon
It's much the same as extending Thread rather than implementing Runnable. You're not changing the functionality of JPanel, you're simply adding components to it.
Anon
@Anon - your argument is completely wrong and would apply to a "raw" `JPanel` that you add components to just as much as it applies to a subclass of `JPanel`
oxbow_lakes
Subclassing Thread and JPanel are examples of implementation inheritence but do not violate the Liskov Substitution Principle as they do not: Strengthen pre-conditions, weaken post-conditions, break invariants or violate the history rule. Regarding your question, yes you could substitute a bespoke JPanel subclass into another application: The application appearance would obviously differ but any API calls would function in the same way and this is what the Liskov principle is concerned with.
Adamski
A: 

The FlowLayout just places component one beside the other in a left-to-right order. When the width reaches the one of the container that has that layout it simply wraps on the other line.

If you want to arrange them in a grid-style layout (like it seems you want) you can use the GridLayout that allows you to specify the number of columns and rows:

component.setLayout(new GridLayout(2,2))

The only downside of GridLayout is that every cell of the grid will be of the same size (which is usually good if you just have JButtons or JLabels but when you mix things it will be visually bad).

If you really need more power go with the GridBagLayout, very customizable but with a steeper learning curve at the beginning.

Probably your size problem is related to the fact that you are using setSize but in Swing these things have strange behaviours, you should try by setting setPreferredSize(200,200) instead of setSize. But don't ask me why!

NOTE: you should ALWAYS refer to the frame's content pane and not to the frame it self. When you set layout you should do getContentPane().setLayout(..), when you add items you should do getContentPane().add(..) and so on.

Errata: now every JFrame add, remove, setLayout automatically forward to the content pane.

Jack
There's no longer a need to refer directly to the JFrame's content pane as add, remove and setLayout have been overridden to forward these calls to the content pane.
Adamski
Yes, I had already checked it and edited before your comment :)
Jack