Although Compositor can make Swing UI generation easier, it can still be difficult to make the layout managers do what you want. I'll try to describe some things I've learned.
First you need to know how the layout managers available in Compositor work.
There are five of them, plus the grid
tag, and here they are with a quick attempt at diagrams (and apologies if you have difficulties with HTML tables).
The default layout is flow layout. Components are laid out from left to right, flowing to multiple rows when they are too wide for one line.
The components take their preferred size, and any extra space in the container is left empty. There is a noticeable margin around the components.
On resize, the components stay the same size and are re-flowed.
|
|||||
|
Sadly this is perhaps the least useful layout manager.
Although border layout can only contain five components, it's very flexible. The five components are laid out geographically:
North | ||
West |
Centre |
East |
South |
The border components take their natural size, but only in the "across" direction for their border. If a border is empty, it takes up no space. The centre component is sized to fit.
So in a packed dialog with border layout, the size of the dialog is determined by the preferred heights of the north and south components, the preferred widths of the east and west components, and the preferred size of the centre component.
One subsidiary use for border layout is to make a panel that grows to fill the space available. No contents are necessary.
On resize, the centre component width and height change, the width of the north and south components, and the height of the east and west components. This is less complicated than it sounds.
Putting components in a row or column can be done with box layout.
In compositor, these are separated as boxLayoutX
and boxLayoutY
.
boxLayoutX | puts | components | like | this |
boxLayoutY |
puts |
components |
like |
this |
One difference between a row of components in box and flow layouts is that flow layout may split the components into multiple rows, but box layout won't.
If the components in a box layout don't align correctly, try setting alignmentX
or alignmentY
On resize, the layout tries to set every component to its preferred size, but then distributes the increase or decrease among the components. To keep components at the size they prefer, add an empty panel with border layout as the last component.
A card layout puts all of the contained components on top of each other and only the top "card" is visible. It's like a tabbed pane, but with no visible way to switch between the contained components. You have to switch between them programmatically.
This kind of layout is useful if you need to show the user different components depending on the state of the app.
When packed, the container will be big enough to hold that largest of the components. Other components fill the available space.
On resize, all components fill the available space.
If you want to force components to be the same size, you may need grid layout. One time this is handy is if you want a set of buttons all to be the same size. This makes them visually grouped.
components: one |
two |
three |
four (tall) |
By default all the components are touching, but you can explicitly set horizontal and vertical gaps. The example Compositor app SimpleEdit's pointless dialogs include one that illustrates various usages of grid layout.
On resize, the components are all resized. As this is probably not what you want, it's worth preventing a grid layout panel from resizing.
If you want components in rows and columns like an HTML table, Compositor has grid
, row
and cell
tags that do this for you.
Unlike grid layout, the columns are not all the same with, and the rows not the same height.
components: one | two |
three |
four (tall) |
On resize, the components are all resized. As this is probably not what you want, it's worth preventing a grid layout panel from resizing.
The second thing that you need to know is how to use these simple tools to achieve what you want. This is harder to explain without knowing what you want, but let's try a couple of examples.
A simple dialog might surround the dialog content with buttons down the right, logo on the left. It would look something like this:
[logo] |
Widgets go here as required for the dialog body |
|
First you need border layout. This will make two side panels the right width for the logo and the buttons, and let any change of size be given to the central area.
If you make the west component a label to hold the logo, it will stay about half way down the LHS as the dialog resizes. But if you wrap it in a panel it will stay at the top - so the default flow layout is useful sometimes after all.
So you have something a bit like this:
<panel layout='borderLayout'> <panel borderLayout='west'><label icon='logo.png' /></panel> <panel> <!-- Widgets here --> </panel> <panel borderLayout='east'><!-- Buttons here --> </panel> </panel>
Now we need to add buttons. You could use box layout Y, but buttons will vary in size. To make them the same width, use grid layout. You have to specify how many rows and columns you want - zero means "as many as necessary". Also specifying some space between the buttons makes them look better.
You should have something like this:
<panel layout='borderLayout'> <panel borderLayout='west'><label icon='logo.png' /></panel> <panel> <!-- Widgets here --> </panel> <panel layout='gridLayout' gridRows='0' gridCols='1' gridHGap='0' gridVGap='6'> <button>OK</button> <button>Cancel</button> </panel> </panel>
Oops - when you resize the dialog, the buttons size with it. To make them stay still, wrap them in another panel:
<panel layout='borderLayout'> <panel borderLayout='west'><label icon='logo.png' /></panel> <panel> <!-- Widgets here --> </panel> <panel borderLayout='east'> <panel layout='gridLayout' gridRows='0' gridCols='1' gridHGap='0' gridVGap='6'> <button>OK</button> <button>Cancel</button> </panel> </panel> </panel>
And that was flow layout being useful again. I was to hard on it earlier.
Finally, add your dialog widgets...
Another common dialog style has buttons down the middle, between two equally sized sides. It looks a bit like this:
|
|
|
This is a UI style often used for configuring something: for example, adding buttons to a toolbar. The two sides represent the possible and the actual, and there are buttons in the middle to move things between the two.
The first thing that will help us is to notice that the three main areas are arranged horizontally, which suggests that we could use box layout X. Bearing in mind how we had to set up the buttons in the previous example, we can skip straight to this:
<panel layout='boxLayoutX'> <scrollpane> <label>list of available things</label> </scrollpane> <panel> <panel layout='gridLayout' gridRows='0' gridCols='1' gridHGap='0' gridVGap='6'> <button>OK</button> <button>Cancel</button> </panel> </panel> <scrollpane> <label>list of things in use</label> </scrollpane> </panel>
Resizing this adds space to all three elements, but nesting border layout panels helps (because the changes in width are absorbed by the two border layout panels, leaving the central panel at its default width):
<panel layout='boxLayoutX'> <panel layout='borderLayout'> <scrollpane><label>list of available things</label></scrollpane> </panel> <panel> <panel layout='gridLayout' gridRows='0' gridCols='1' gridHGap='0' gridVGap='6'> <button>OK</button> <button>Cancel</button> </panel> </panel> <panel layout='borderLayout'> <scrollpane><label>list of things in use</label></scrollpane> </panel> </panel>
But the two sides are not equal sizes - they both fit their content. To force equal size, we can specify preferred heights and widths:
<panel layout='boxLayoutX'> <panel layout='borderLayout'> <scrollpane preferredWidth='100' preferredHeight='100'> <label>list of available things</label> </scrollpane> </panel> <panel> <panel layout='gridLayout' gridRows='0' gridCols='1' gridHGap='0' gridVGap='6'> <button>OK</button> <button>Cancel</button> </panel> </panel> <panel layout='borderLayout'> <scrollpane preferredWidth='100' preferredHeight='100'> <label>list of things in use</label> </scrollpane> </panel> </panel>
The general approach is to think which layout manager will give you the basic look you want, then to do the same thing with the contents of each area you've defined. Adding wrapper panels is a common solution to many layout issues.
There are also a few things that it took me a while to really believe.
JPanel
instances, the overhead of more of them is not so much.
I've run Compositor apps on fairly slow machines with fairly good results.