TableLayout is a layout manager that is more powerful than GridBagLayout
yet much easier to use.
Background
TableLayout is a layout manager that arranges components in rows and
columns like a spreadsheet. TableLayout allows each row or column to be a
different size. A row or column can be given an absolute size in pixels, a
percentage of the available space, or it can grow and shrink to fill the
remaining space after other rows and columns have been resized.
Using spreadsheet terminology, a cell is the intersection of a row and
column. Cells have finite, non-negative sizes measured in pixels. The
dimensions of a cell depend solely upon the dimensions of its row and column.
A component occupies a rectangular group of one or more cells. The
component can be aligned within those cells using four vertical and six
horizontal justifications. The vertical justifications are left, center,
right, and full. The horizontal justifications are left, center, right,
full, leading, and trailing. With full justification the component is
stretched either vertically or horizontally to fit the cell or group of
cells.
Justification
Leading and trailing justification are used to support languages that
are read from right to left. See the
java.awt.ComponentOrientation
class for details and
http://java.sun.com/products/jfc/tsc/articles/bidi for an introduction to
component orientation and bidirectional text support. The leading
justification will align the component along the leading edge of the
container and the trailing justification will align the component along the
trailing edge. There is no leading or trailing justification along the
vertical axis since all modern languages are read from top to bottom and
no bottom-to-top orientation is defined in
java.awt.ComponentOrientation.
For components using the
ComponentOrientation.LEFT_TO_RIGHT
orientation, the leading edge is the left edge and the trailing edge is the
right one. For components using the
ComponentOrientation.RIGHT_TO_LEFT
orientation, the opposite is true. For components that are using
ComponentOrientation.UNKNOWN
and for Java runtime environments
that do not support component orientation, left-to-right orientation is
assumed for backwards compatibility.
Gaps
Horizontal and vertical gaps can be placed between rows and columns in two
ways. If uniformed gaps are desired, the
setHGap
and
setVGap
methods may be used. To vary the size of gaps, simply use
empty rows and columns with absolute sizes. Similiarly, to make a border
around a container that does not have insets, use empty rows and columns
along the edges of the container.
Constraints
Using TableLayout is a simple two step process. First, create a grid for
your container by specifying row and column sizes using either a TableLayout
constructor or the
insertRow
and
insertColumn
methods. Second, add components to the cells formed by the rows and
columns.
When adding a component to a container that uses TableLayout, you
specify the component's constraints that state which cells the component
will occupy and how the component will be aligned. The constraints
can be specified into two ways. The
TableLayoutConstraints
class can be used to systematically specify the constraints. This is
useful to dynamic code, bean builders, and rapid application development
software.
For manual coding, a quicker and easier way to specify constraints is with
a short string in the form "x1, y1, x2, y2, hAlign, vAlign" where (x1, y1)
identifies the top left cell (column x1, row y1) for the component and
(x2, y2) identfies the bottom right cell. x2 and y2 are optional. If they
are not specified, the component will occupy only one cell, (x1, y1).
hAlign and vAlign are also optional with default values of full
justification. Alignments may be spelt fully as in "LEFT" or abbreviated as
in "L". The text is not case sensitive, but it is recommended that uppercase
is used for two reasons. First, these text values are in essence constants.
Second, some fonts use the same glyphs for representing a lowercase L and
the number one. Ex., "l" vs. "1". Even fonts that do not will often use
similar glyphs so using uppercase avoids confusion.
Dynamically altering the layout
Rows and columns can be dynamically created, resized, and removed at any
time, even if the container is visible. Components will be shifted
appropriately as rows and columns are inserted or removed, just as cells
are shifted in a spreadsheet.
Rows and columns can be made "hidden" or effectively invisible by setting
their size to zero. They can be shown again by setting their size back to
a non-zero value. This is very useful for toggle form elements without
having to remove individual components.
Preferred sizes
Often it is desireable to make a row or column just large enough to ensure
that all components contained partially or wholly in that row or column are
their preferred size. To make this easy, there is a constant called
PREFERRED
that can be used to specify row or column sizes.
There is another constant called
MINIMUM
that does a similar
task using components' minimum sizes instead of their preferred sizes.
There is no corresponding
MAXIMUM
constant for several
reasons. First, it is mathematically impossible to honor both the minimum
and maximum sizes of more than one component when conflicts arise. For
example, say components a and b are in the same row. If a's maximum height
is less than b's minimum height, then one of these constraints must be
violated. Since TableLayout is a complete, general Cartesian layout manager,
it would be possible to specify conflicting constraints if a
MAXIMUM
constant existed.
Second, the ability to make a component grow up to a maximum size is
primarily of interest to layout managers like
SpringLayout
that
have to balance the sizes of components because the presence of one component
affects the size of another. Other than the effect of preferred and minimum
size rows/columns, which are essentially convenient ways of specifying
absolute sizes, the existence and constraints of one component does not
affect any other components when using TableLayout. This is accomplished
because rows and columns are explicit in TableLayout.
Third, the ability to constrain a component to its maximum size is
subsumed by the ability to constrain it to its preferred size, which is
precisely what happens when a component is aligned using anything but
full justification. In the case of full justification, the component's
maximum size is by definition unbounded.
Example
import java.awt.*;
import javax.swing.*;
import info.clearthought.layout.TableLayout;
public class Preferred extends JFrame
{
public static void main (String args[])
{
new Preferred();
}
public Preferred ()
{
super("The Power of Preferred Sizes");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = getContentPane();
// b - border
// f - FILL
// p - PREFERRED
// vs - vertical space between labels and text fields
// vg - vertical gap between form elements
// hg - horizontal gap between form elements
double b = 10;
double f = TableLayout.FILL;
double p = TableLayout.PREFERRED;
double vs = 5;
double vg = 10;
double hg = 10;
double size[][] =
{{b, f, hg, p, hg, p, b},
{b, p, vs, p, vg, p, vs, p, vg, p, vs, p, vg, p, b}};
TableLayout layout = new TableLayout(size);
pane.setLayout (layout);
// Create all controls
JLabel labelName = new JLabel("Name");
JLabel labelAddress = new JLabel("Address");
JLabel labelCity = new JLabel("City");
JLabel labelState = new JLabel("State");
JLabel labelZip = new JLabel("Zip");
JTextField textfieldName = new JTextField(10);
JTextField textfieldAddress = new JTextField(20);
JTextField textfieldCity = new JTextField(10);
JTextField textfieldState = new JTextField(2);
JTextField textfieldZip = new JTextField(5);
JButton buttonOk = new JButton("OK");
JButton buttonCancel = new JButton("Cancel");
JPanel panelButton = new JPanel();
panelButton.add(buttonOk);
panelButton.add(buttonCancel);
// Add all controls
pane.add(labelName, "1, 1, 5, 1");
pane.add(textfieldName, "1, 3, 5, 3");
pane.add(labelAddress, "1, 5, 5, 5");
pane.add(textfieldAddress, "1, 7, 5, 7");
pane.add(labelCity, "1, 9");
pane.add(textfieldCity, "1, 11");
pane.add(labelState, "3, 9");
pane.add(textfieldState, "3, 11");
pane.add(labelZip, "5, 9");
pane.add(textfieldZip, "5, 11");
pane.add(panelButton, "1, 13, 5, 13");
pack();
setResizable(false);
show();
}
}
addLayoutComponent
public void addLayoutComponent(Component component,
Object constraint)
Adds the specified component with the specified name to the layout.
component
- component to addconstraint
- indicates entry's position and alignment
addLayoutComponent
public void addLayoutComponent(String name,
Component component)
Adds the specified component with the specified name to the layout.
name
- indicates entry's position and anchorcomponent
- component to add
assignAbsoluteSize
protected int assignAbsoluteSize(int z,
int availableSize)
Assigns absolute sizes.
z
- indicates row or columnavailableSize
- amount of space available in the container
- the amount of space available after absolute crs have been assigned
sizes
assignFillSize
protected void assignFillSize(int z,
int availableSize)
Assigns FILL sizes.
z
- indicates row or columnavailableSize
- amount of space available in the container
assignPrefMinSize
protected int assignPrefMinSize(int z,
int availableSize,
double typeOfSize)
Assigned sizes to preferred and minimum size columns and rows. This
reduces the available width and height. Minimum widths/heights must be
calculated first because they affect preferred widths/heights, but not vice
versa. The end result is that any component contained wholly or partly in
a column/row of minimum/preferred width or height will get at least its
minimum/preferred width or height, respectively.
z
- indicates row or columnavailableSize
- amount of space available in the containertypeOfSize
- indicates preferred or minimum
- the amount of space available after absolute crs have been assigned
sizes
assignRelativeSize
protected int assignRelativeSize(int z,
int availableSize)
Assigns relative sizes.
z
- indicates row or columnavailableSize
- amount of space available in the container
- the amount of space available after relative crs have been assigned
sizes
calculateLayoutSize
protected Dimension calculateLayoutSize(Container container,
double typeOfSize)
Calculates the preferred or minimum size for the methods preferredLayoutSize
and minimumLayoutSize.
container
- container whose size is being calculatedtypeOfSize
- indicates preferred or minimum
- a dimension indicating the container's preferred or minimum size
calculateLayoutSize
protected int calculateLayoutSize(Container container,
int z,
double typeOfSize,
entryList[] ,
prefMinSize[] )
Calculates the preferred or minimum size for the method
calculateLayoutSize(Container container, double typeOfSize). This method
is passed the preferred/minimum sizes of the components so that the
potentially expensive methods getPreferredSize()/getMinimumSize() are not
called twice for the same component.
container
- container whose size is being calculatedz
- indicates row or columntypeOfSize
- indicates preferred or minimum
- a dimension indicating the container's preferred or minimum size
calculateOffset
protected void calculateOffset(int z,
Insets inset)
Calculates the offset of each cr.
z
- indicates row or column
calculateSize
protected void calculateSize(Container container)
Calculates the sizes of the rows and columns based on the absolute and
relative sizes specified in crSpec[R]
and crSpec[C]
and the size of the container. The result is stored in crSize[R]
and crSize[C]
.
container
- container using this TableLayout
calculateSizeAndOffset
protected int[] calculateSizeAndOffset(TableLayout.Entry entry,
int preferredSize,
boolean isColumn)
Calculates the vertical/horizontal offset and size of a component.
entry
- entry containing component and contraintspreferredSize
- previously calculated preferred width/height of
componentisColumn
- if true, this method is being called to calculate
the offset/size of a column. if false,... of a row.
- an array, a, of two integers such that a[0] is the offset and
a[1] is the size
deleteColumn
public void deleteColumn(int i)
Deletes a column in this layout. All components to the right of the
deletion point are moved left one column. The container will need to
be laid out after this method returns. See setColumn
.
i
- zero-based index of column to delete
deleteCr
protected void deleteCr(int z,
int i)
Deletes a cr for the methods deleteRow or deleteColumn.
z
- indicates row or columni
- zero-based index of cr to delete
deleteRow
public void deleteRow(int i)
Deletes a row in this layout. All components below the deletion point are
moved up one row. The container will need to be laid out after this method
returns. See setRow
. There must be at least two rows in order
to delete a row.
i
- zero-based index of row to delete
getColumn
public double[] getColumn()
Gets the sizes of columns in this layout.
- widths of each of the columns
getColumn
public double getColumn(int i)
Gets the width of a single column in this layout.
i
- zero-based index of row to get. If this parameter is not valid,
an ArrayOutOfBoundsException will be thrown.
- width of the requested column
getComponentOrientation
protected ComponentOrientation getComponentOrientation(Container container)
Gets the container's component orientation. If a JDK that does not support
component orientation is being used, then null is returned.
container
- Container whose orientation is being queried
- the container's orientation or null if no orientation is supported
getConstraints
public TableLayoutConstraints getConstraints(Component component)
Gets the constraints of a given component.
component
- desired component
- If the given component is found, the constraints associated with
that component. If the given component is null or is not found,
null is returned.
getHGap
public int getHGap()
Gets the horizontal gap between colunns.
- the horizontal gap in pixels
getInvalidEntry
public java.util.List getInvalidEntry()
Determines whether or not there are any components with invalid constraints.
An invalid constraint is one that references a non-existing row or column.
For example, on a table with five rows, row -1 and row 5 are both invalid.
Valid rows are 0 through 4, inclusively. This method is useful for
debugging.
- a list of TableLayout.Entry instances refering to the invalid
constraints and corresponding components
getLayoutAlignmentX
public float getLayoutAlignmentX(Container parent)
Returns the alignment along the x axis. This specifies how the component
would like to be aligned relative to other components. The value should be
a number between 0 and 1 where 0 represents alignment along the origin, 1 is
aligned the furthest away from the origin, 0.5 is centered, etc.
getLayoutAlignmentY
public float getLayoutAlignmentY(Container parent)
Returns the alignment along the y axis. This specifies how the component
would like to be aligned relative to other components. The value should be
a number between 0 and 1 where 0 represents alignment along the origin, 1 is
aligned the furthest away from the origin, 0.5 is centered, etc.
getNumColumn
public int getNumColumn()
Gets the number of columns in this layout.
getNumRow
public int getNumRow()
Gets the number of rows in this layout.
getOverlappingEntry
public java.util.List getOverlappingEntry()
Gets a list of overlapping components and their constraints. Two
components overlap if they cover at least one common cell. This method is
useful for debugging.
- a list of zero or more TableLayout.Entry instances
getRow
public double[] getRow()
Gets the height of a single row in this layout.
- height of the requested row
getRow
public double getRow(int i)
Gets the sizes of a row in this layout.
i
- zero-based index of row to get. If this parameter is not valid,
an ArrayOutOfBoundsException will be thrown.
- height of each of the requested row
getVGap
public int getVGap()
Gets the vertical gap between rows.
- the vertical gap in pixels
init
protected void init(double[] col,
double[] row)
Initializes the TableLayout for all constructors.
col
- widths of columns in the format, {{col0, col1, col2, ..., colN}row
- heights of rows in the format, {{row0, row1, row2, ..., rowN}
insertColumn
public void insertColumn(int i,
double size)
Inserts a column in this layout. All components to the right of the
insertion point are moved right one column. The container will need to
be laid out after this method returns. See setColumn
.
i
- zero-based index at which to insert the columnsize
- size of the column to be inserted
insertCr
public void insertCr(int z,
int i,
double size)
Inserts a cr for the methods insertRow or insertColumn.
z
- indicates row or columni
- zero-based index at which to insert the crsize
- size of cr being inserted
insertRow
public void insertRow(int i,
double size)
Inserts a row in this layout. All components below the insertion point
are moved down one row. The container will need to be laid out after this
method returns. See setRow
.
i
- zero-based index at which to insert the rowsize
- size of the row to be inserted
invalidateLayout
public void invalidateLayout(Container target)
Invalidates the layout, indicating that if the layout manager has cached
information it should be discarded.
layoutContainer
public void layoutContainer(Container container)
To lay out the specified container using this layout. This method reshapes
the components in the specified target container in order to satisfy the
constraints of all components.
User code should not have to call this method directly.
container
- container being served by this layout manager
maximumLayoutSize
public Dimension maximumLayoutSize(Container target)
Returns the maximum dimensions for this layout given the components in the
specified target container.
target
- the component which needs to be laid out
- unconditionally, a Dimension of Integer.MAX_VALUE by
Integer.MAX_VALUE since TableLayout does not limit the
maximum size of a container
minimumLayoutSize
public Dimension minimumLayoutSize(Container container)
Determines the minimum size of the container argument using this layout.
The minimum size is the smallest size that, if used for the container's
size, will ensure that all components are at least as large as their
minimum size. This method cannot guarantee that all components will be
their minimum size. For example, if component A and component B are each
allocate half of the container's width and component A wants to be 10 pixels
wide while component B wants to be 100 pixels wide, they cannot both be
accommodated. Since in general components rather be larger than their
minimum size instead of smaller, component B's request will be fulfilled.
The minimum size of the container would be 200 pixels.
container
- container being served by this layout manager
- a dimension indicating the container's minimum size
preferredLayoutSize
public Dimension preferredLayoutSize(Container container)
Determines the preferred size of the container argument using this layout.
The preferred size is the smallest size that, if used for the container's
size, will ensure that all components are at least as large as their
preferred size. This method cannot guarantee that all components will be
their preferred size. For example, if component A and component B are each
allocate half of the container's width and component A wants to be 10 pixels
wide while component B wants to be 100 pixels wide, they cannot both be
accommodated. Since in general components rather be larger than their
preferred size instead of smaller, component B's request will be fulfilled.
The preferred size of the container would be 200 pixels.
container
- container being served by this layout manager
- a dimension indicating the container's preferred size
removeLayoutComponent
public void removeLayoutComponent(Component component)
Removes the specified component from the layout.
component
- component being removed
setColumn
public void setColumn(column[] )
Adjusts the number and sizes of rows in this layout. After calling this
method, the caller should request this layout manager to perform the
layout. This can be done with the following code:
layout.layoutContainer(container);
container.repaint();
or
window.pack()
If this is not done, the changes in the layout will not be seen until the
container is resized.
setColumn
public void setColumn(int i,
double size)
Adjusts the width of a single column in this layout. After calling this
method, the caller should request this layout manager to perform the
layout. This can be done with the following code:
layout.layoutContainer(container);
container.repaint();
or
window.pack()
If this is not done, the changes in the layout will not be seen until the
container is resized.
i
- zero-based index of column to set. If this parameter is not
valid, an ArrayOutOfBoundsException will be thrown.size
- width of the column. This parameter cannot be null.
setConstraints
public void setConstraints(Component component,
TableLayoutConstraints constraint)
Sets the constraints of a given component.
component
- desired component. This parameter cannot be null.constraint
- new set of constraints. This parameter cannot be null.
setCr
protected void setCr(int z,
int i,
double size)
Sets the sizes of rows or columns for the methods setRow or setColumn.
z
- indicates row or columni
- indicates which cr to resizesize
- new cr size
setCr
protected void setCr(int z,
size[] )
Sets the sizes of rows or columns for the methods setRow or setColumn.
z
- indicates row or column
setHGap
public void setHGap(int hGap)
Sets the horizontal gap between colunns.
hGap
- the horizontal gap in pixels
setRow
public void setRow(int i,
double size)
Adjusts the height of a single row in this layout. After calling this
method, the caller should request this layout manager to perform the
layout. This can be done with the following code:
layout.layoutContainer(container);
container.repaint();
or
window.pack()
If this is not done, the changes in the layout will not be seen until the
container is resized.
i
- zero-based index of row to set. If this parameter is not
valid, an ArrayOutOfBoundsException will be thrown.size
- height of the row. This parameter cannot be null.
setRow
public void setRow(row[] )
Adjusts the number and sizes of rows in this layout. After calling this
method, the caller should request this layout manager to perform the
layout. This can be done with the following code:
layout.layoutContainer(container);
container.repaint();
or
window.pack()
If this is not done, the changes in the layout will not be seen until the
container is resized.
setVGap
public void setVGap(int vGap)
Sets the vertical gap between rows.
vGap
- the horizontal gap in pixels
toString
public String toString()
Converts this TableLayout to a string.
- a string representing the columns and row sizes in the form
"{{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}"