AWT/Swing layout behavior?

P

Peter Duniho

I am having trouble figuring out the right way to use the GridBagLayout
class.

I have some sample code (see below) that I _expected_ would take the two
components added to the frame and always make them the same size, arranged
vertically.

What happens instead is that when one of the two components is a Panel or
JPanel (and possibly other container types...those are the two I tested),
if that component contains another component, it takes up more than half
of the frame.

Reading the documentation, I am under the impression that the
GridBagLayout class automatically allocates the dimensions of a given
component according to the weight assigned to it in the GridBagConstraints
class set for the component. Thus, since these two components have the
same weights, they should always be equally sized. But for some reason,
having the container actually contain something affects this.

The sample code only adds one item, and the difference in size is
noticeable but not very large. However, this came up in a more complex
situation, where a larger number of controls are added. In that case, the
size difference is proportionally larger as well, resulting in effective
relative weights more like 3 or 4 to 1.

Is there a way to get the GridBagLayout class, or some other built-in
LayoutManager, to do this sort of auto-sizing where the relative weights
will always actually stay equal?

Thanks!
Pete

p.s. Something I ran into while putting the sample code together is that
sometimes when I'd run the code, the button would not show up. It'd be
briefly visible, and it would show up again once the window is resized.
But to start with, it's not visible. Is there some basic flaw in the way
I'm creating this UI that is leading to this erratic behavior? Or am I
just looking at some sort of run-time bug?



import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;


public class TestPanel
{

/**
* @param args
*/
public static void main(String[] args)
{
Frame frame = new Frame("TestPanel");
GridBagLayout grid = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
JPanel panel;

frame.setLayout(grid);

constraints.fill = GridBagConstraints.BOTH;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.weightx = 1;
constraints.weighty = 1;

panel = new JPanel();
panel.setBorder(new LineBorder(Color.BLACK));
panel.add(new Button("Test"));
frame.add(panel);
grid.setConstraints(panel, constraints);

panel = new JPanel();
panel.setBorder(new LineBorder(Color.BLACK));
frame.add(panel);
grid.setConstraints(panel, constraints);

frame.setSize(240, 480);
frame.setVisible(true);
}
}
 
K

Knute Johnson

Peter said:
I am having trouble figuring out the right way to use the GridBagLayout
class.

I have some sample code (see below) that I _expected_ would take the two
components added to the frame and always make them the same size,
arranged vertically.

What happens instead is that when one of the two components is a Panel
or JPanel (and possibly other container types...those are the two I
tested), if that component contains another component, it takes up more
than half of the frame.

Reading the documentation, I am under the impression that the
GridBagLayout class automatically allocates the dimensions of a given
component according to the weight assigned to it in the
GridBagConstraints class set for the component. Thus, since these two
components have the same weights, they should always be equally sized.
But for some reason, having the container actually contain something
affects this.

The sample code only adds one item, and the difference in size is
noticeable but not very large. However, this came up in a more complex
situation, where a larger number of controls are added. In that case,
the size difference is proportionally larger as well, resulting in
effective relative weights more like 3 or 4 to 1.

Is there a way to get the GridBagLayout class, or some other built-in
LayoutManager, to do this sort of auto-sizing where the relative weights
will always actually stay equal?

Thanks!
Pete

p.s. Something I ran into while putting the sample code together is
that sometimes when I'd run the code, the button would not show up.
It'd be briefly visible, and it would show up again once the window is
resized. But to start with, it's not visible. Is there some basic flaw
in the way I'm creating this UI that is leading to this erratic
behavior? Or am I just looking at some sort of run-time bug?



import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;


public class TestPanel
{

/**
* @param args
*/
public static void main(String[] args)
{
Frame frame = new Frame("TestPanel");
GridBagLayout grid = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
JPanel panel;

frame.setLayout(grid);

constraints.fill = GridBagConstraints.BOTH;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.weightx = 1;
constraints.weighty = 1;

panel = new JPanel();
panel.setBorder(new LineBorder(Color.BLACK));
panel.add(new Button("Test"));
frame.add(panel);
grid.setConstraints(panel, constraints);

panel = new JPanel();
panel.setBorder(new LineBorder(Color.BLACK));
frame.add(panel);
grid.setConstraints(panel, constraints);

frame.setSize(240, 480);
frame.setVisible(true);
}
}

I didn't try your program but I'll give you a couple of clues. The
weights only effect extra space. Once you figure out what that actually
means, using GBL becomes much easier. It is usually easier to pack the
container than to set it to a particular size. With GBL if you put more
into the container than will fit, the layout manager will squash some or
all of the components to invisibility. You can make that happen by
resizing your container smaller. GBL is pretty good at following
preferred size, so setting that on your components can help you layout
your GUI.

If you want a grid style layout manager that makes all components the
same size you can use GridLayout. If you are using it for controls it
is pretty ugly but for containers in containers it is a nice simple
layout manager.


I should really write a tutorial for GBL. It is a really powerful
layout manager but also very complex with a lot of subtle features.
 
D

Daniele Futtorovic

I am having trouble figuring out the right way to use the GridBagLayout
class.

I have some sample code (see below) that I _expected_ would take the two
components added to the frame and always make them the same size,
arranged vertically.

What happens instead is that when one of the two components is a Panel
or JPanel (and possibly other container types...those are the two I
tested), if that component contains another component, it takes up more
than half of the frame.

Reading the documentation, I am under the impression that the
GridBagLayout class automatically allocates the dimensions of a given
component according to the weight assigned to it in the
GridBagConstraints class set for the component. Thus, since these two
components have the same weights, they should always be equally sized.
But for some reason, having the container actually contain something
affects this.

The sample code only adds one item, and the difference in size is
noticeable but not very large. However, this came up in a more complex
situation, where a larger number of controls are added. In that case,
the size difference is proportionally larger as well, resulting in
effective relative weights more like 3 or 4 to 1.

Gridbag weight determines how /extra space/ is distributed among the
components. For the initial size, minimum, preferred and maximum size
are taken into consideration.
Is there a way to get the GridBagLayout class, or some other built-in
LayoutManager, to do this sort of auto-sizing where the relative weights
will always actually stay equal?

GridBagLayout... I don't think so, not without major contortions. What
you need is probably a simple GridLayout. Have a look at the BoxLayout
and the associated Box, too. If you only have two components, you might
also use a SplitPane.
Layout managers here:
p.s. Something I ran into while putting the sample code together is
that sometimes when I'd run the code, the button would not show up.
It'd be briefly visible, and it would show up again once the window is
resized. But to start with, it's not visible. Is there some basic flaw
in the way I'm creating this UI that is leading to this erratic
behavior? Or am I just looking at some sort of run-time bug?

Can't tell you about the erratic behaviour precisely. There are a few
problems with your code, however.

1. Don't mix AWT and Swing components. I would like to put this in a
less dogmatic way, but I'm not sure there is much point to it. Perhaps I
rather ought to say "avoid mixing them at all costs". But I for one
haven't ever had to -- or if I have, I don't remember.
This applies only to widgets, of course, not layout managers, which are
mainly in the AWT package.
Use JFrame instead of Frame, JButton instead of Button, etc.. It may be
the erratic behaviour you described was due to this.

2.
> frame.add(panel);

Do not add components to the frame directly, but rather to its content
pane (javax.swing.JFrame#getContentPane).

3.
> frame.add(panel);
> grid.setConstraints(panel, constraints);

Consider using the java.awt.Container#add(Component, Object) method. You
would thus avoid the call to GridBagLayout#setConstraints. Basically,
when filling a GridBagLayout, you never need a reference to the layout
object.

4.
> panel.setBorder(new LineBorder(Color.BLACK));

Borders are preferably obtained via the javax.swing.BorderFactory class.



One last general advice: GridBagLayout is a powerful beast, but it's not
a replacement for good container nesting. Do not hesitate to use
multiple nested containers, each with an appropriate layout, rather than
trying to fit a complex layout fully into one GridBagLayout.

DF.
 
P

Peter Duniho

Gridbag weight determines how /extra space/ is distributed among the
components. For the initial size, minimum, preferred and maximum size
are taken into consideration.

So, if of the two components, one has a larger minimum and/or preferred
size than the other, then even if the weights are identical, they will not
be sized identically? This would explain what I'm seeing.
GridBagLayout... I don't think so, not without major contortions. What
you need is probably a simple GridLayout. Have a look at the BoxLayout
and the associated Box, too. If you only have two components, you might
also use a SplitPane.
Layout managers here:
<http://java.sun.com/docs/books/tutorial/uiswing/layout/index.html>

Yes, I know where they are. :)

It looks like GridLayout is useful here. I had already tried BoxLayout
previously, and it does the same thing that GridBagLayout does.
GridLayout, on the other hand, appears to ignore the sizing preferences of
the contained components, and so in my case does the right thing. Thanks!
Can't tell you about the erratic behaviour precisely. There are a few
problems with your code, however.

1. Don't mix AWT and Swing components. I would like to put this in a
less dogmatic way, but I'm not sure there is much point to it. Perhaps I
rather ought to say "avoid mixing them at all costs". But I for one
haven't ever had to -- or if I have, I don't remember.
This applies only to widgets, of course, not layout managers, which are
mainly in the AWT package.
Use JFrame instead of Frame, JButton instead of Button, etc.. It may be
the erratic behaviour you described was due to this.

Interesting. The only reason I used JPanel instead of Panel for the
sample code was that I wanted to see the border, and I don't know a
convenient way to set the border of a Panel. But you're right...at least
on a quick test (20-30 executions) I was unable to reproduce the "missing
button" behavior as long as I was using a Panel instead of a JPanel.

I am surprised at the incompatibility. I was under the impression that
Swing was built on AWT, implying that the two would work nicely together.
But apparently they don't.
2.

Do not add components to the frame directly, but rather to its content
pane (javax.swing.JFrame#getContentPane).

I assume this applies only to Swing JFrames, and not AWT Frames. I don't
even see a similar method in the AWT Frame.

That said, the docs say that JFrame.add() forwards to the content pane.
So while they do agree with you that components should be added to the
content pane, it appears that one would have to go out of one's way in
order to not do so. Just calling the usual "add" methods would work fine,
as near as I can tell.
3.

Consider using the java.awt.Container#add(Component, Object) method. You
would thus avoid the call to GridBagLayout#setConstraints. Basically,
when filling a GridBagLayout, you never need a reference to the layout
object.

Okay, sounds good. I didn't even realize the other "add" method existed.
You're right, that's much more convenient. :)
4.

Borders are preferably obtained via the javax.swing.BorderFactory class.

Why? Of the (at least) three different ways I know of to make a border,
that seems like the least convenient. What's the advantage in using it?
One last general advice: GridBagLayout is a powerful beast, but it's not
a replacement for good container nesting. Do not hesitate to use
multiple nested containers, each with an appropriate layout, rather than
trying to fit a complex layout fully into one GridBagLayout.

Point taken. Thanks!

Pete
 
P

Peter Duniho

[...]
If you want a grid style layout manager that makes all components the
same size you can use GridLayout. If you are using it for controls it
is pretty ugly but for containers in containers it is a nice simple
layout manager.

Thanks...per Daniele's advice as well, it looks like that's the way to go
in this case.

Pete
 
D

Daniele Futtorovic

Interesting. The only reason I used JPanel instead of Panel for the
sample code was that I wanted to see the border, and I don't know a
convenient way to set the border of a Panel. But you're right...at
least on a quick test (20-30 executions) I was unable to reproduce
the "missing button" behavior as long as I was using a Panel instead
of a JPanel.

I am surprised at the incompatibility. I was under the impression
that Swing was built on AWT, implying that the two would work nicely
together. But apparently they don't.

Swing is built on AWT /classes/, but the connection with the OS is
different. There's a whole lot of coverage for this topic on the net,
and in a healthy spirit of minimizing personal strain and general
blunder by citing authoritative sources, here's some:
I assume this applies only to Swing JFrames, and not AWT Frames. I
don't even see a similar method in the AWT Frame.

Yes, this applies only to JFrame.
That said, the docs say that JFrame.add() forwards to the content
pane. So while they do agree with you that components should be added
to the content pane, it appears that one would have to go out of
one's way in order to not do so. Just calling the usual "add"
methods would work fine, as near as I can tell.

You got me there. Indeed, they added this behaviour back in 1.5, so I
really should have caught up. :)
Why? Of the (at least) three different ways I know of to make a
border, that seems like the least convenient. What's the advantage
in using it?

Well, for one you don't need to import javax.swing.border. One other
point would be that, as they note in the docs: "Wherever possible, this
factory will hand out references to shared Border instances". For an
entirely hypothetical scenario, one could imagine a future release using
different classes for implementation.
All in all it's not that much of a difference, I agree.

I'm missing something, though. Aside from BorderFactory and direct
instantiation, what's the third way of getting Border Objects you're
speaking of?

DF.
 
P

Peter Duniho

Swing is built on AWT /classes/, but the connection with the OS is
different. There's a whole lot of coverage for this topic on the net,
and in a healthy spirit of minimizing personal strain and general
blunder by citing authoritative sources, here's some:
<http://java.sun.com/products/jfc/tsc/articles/mixing/index.html>

I see. From that article, it appears that the main concern is how AWT
controls can stomp on top of Swing controls, because they are OS objects
while the Swing controls are not. That doesn't explain the behavior I was
seeing per se, but it does suggest that if there's some degree of
non-deterministic behavior (and I suppose depending on the order in which
the OS hands out paint messages, there could be), that might explain why
sometimes a control might not show up.

Though actually, in my case it was an AWT control not showing up when it
was contained within a JFrame. In other words, two heavyweight controls.
But I suppose there might be some other subtle interactions. Or rather,
obviously there must be. :)
[...]
Well, for one you don't need to import javax.swing.border. One other
point would be that, as they note in the docs: "Wherever possible, this
factory will hand out references to shared Border instances".

Ah, good point. Since that's a common reason for a factory to exist, I
should have been able to figure that out. :)
I'm missing something, though. Aside from BorderFactory and direct
instantiation, what's the third way of getting Border Objects you're
speaking of?

That was my mistake. For some reason, I thought that the BorderFactory
was a class I'd never heard of. But it turns out, it's what I was using
in some other code where I called the createXXX methods.

So when you wrote about the BorderFactory, I knew I'd already used two
different mechanisms, and you mentioned one that I thought I hadn't heard
of, and adding those all up I got three. :)

I was able to get my layout working much better, using the GridLayout. I
may yet revisit the GridBagLayout, because it turns out that it would be
nice to be able to adjust the relative weights beyond the straight
equal-weighting GridLayout offers. But for the time being, it's working
so much better than what I'd accomplished before that I'm not all that
eager to go back to messing with GridBagLayout.

For what it's worth, one thing I discovered when I went through and made
everything Swing components was that the vertical and horizontal alignment
defaults appear to be different from AWT. I had some layout code using
the Box class that worked fine with the AWT controls, but not with the
Swing controls. I had to add explicit statements to set the alignment in
order to get things working again.

Just another "gotcha" for the neophyte Java programmer (i.e. me :) ).

Pete
 
D

Daniele Futtorovic

I see. From that article, it appears that the main concern is how
AWT controls can stomp on top of Swing controls, because they are OS
objects while the Swing controls are not. That doesn't explain the
behavior I was seeing per se, but it does suggest that if there's
some degree of non-deterministic behavior (and I suppose depending on
the order in which the OS hands out paint messages, there could be),
that might explain why sometimes a control might not show up.

Though actually, in my case it was an AWT control not showing up when
it was contained within a JFrame. In other words, two heavyweight
controls. But I suppose there might be some other subtle
interactions. Or rather, obviously there must be. :)

Subtle interactions, yeah... like well-defined areas of uncertainty.
Uncertainty in programs is the kind of thing I loathe, so I have never
tried to explore them, sticking to Swing, and consequently can't tell
you much more. But I'd like to stress that, on the other hand, neither
have I ever /had/ to explore them.

That was my mistake. For some reason, I thought that the
BorderFactory was a class I'd never heard of. But it turns out, it's
what I was using in some other code where I called the createXXX
methods.

So when you wrote about the BorderFactory, I knew I'd already used
two different mechanisms, and you mentioned one that I thought I
hadn't heard of, and adding those all up I got three. :)
:)


I was able to get my layout working much better, using the
GridLayout. I may yet revisit the GridBagLayout, because it turns out
that it would be nice to be able to adjust the relative weights
beyond the straight equal-weighting GridLayout offers. But for the
time being, it's working so much better than what I'd accomplished
before that I'm not all that eager to go back to messing with
GridBagLayout.

GridBagLayout is hard to master and requires minute planning of nesting,
weights, anchors, filling and component minimum, maximum and preferred
sizes. In complex cases at least. But once you get it there's hardly
anything you cannot do with it. In fact you have stumbled upon one of
these few cases.
Just another "gotcha" for the neophyte Java programmer (i.e. me :) ).

Well then, good luck.

df.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top