The ArithmeticLayoutManager is a layout manager which can be used to specify the bounds of a component as a series of mathematical expressions. The expressions are evaluated every time the container is resized and consist of simple calculations with references to fields of the containing component and other components within the same container.
ArithmeticLayoutManager was born out of dissatisfaction with existing layout managers. Although some extremely powerful layout managers exist, most of them present the programmer with a horrendous amount of complexity by requiring intricate chains of invocations, an unintuive String syntax, or a complex configuration procedure (or all of the above). As a result most developers simply use the null layout, which decreases the usability of the program. ArithmeticLayoutManager aims combine an intuitive Java/CSS-like syntax with powerful arithmetic expressions.
Lets start by doing a trick that is extremely hard to do in most
layout managers:
Container panel = getContentPane();
panel.setLayout(new ArithmeticLayoutManager());
JLabel nameLabel = new JLabel("Name:");
panel.add(nameLabel,
"name = nameLabel; "+
"top = 20; "+
"left = 20; ");
JTextField nameField = new JTextField();
panel.add(nameField,
"top = 20; "+
"left = nameLabel.rRight + 20; "+
"right = 20; ");
We have now placed a label and a textbox with 20 pixels in between. The exact location of the textbox depends on the size of the label and the textbox automatically resizes with the window. (Web Start Demo - Full source)
ArithmeticLayoutManager (ALM) works by using the
constraints parameter when adding a component. The constraint has
to be a String of the form:
<field> = <expression>;
<field> = <expression>;
...
A field can be one of
left,
top,
right,
bottom,
rleft,
rtop,
rright,
rbottom,
width,
height,
name
or an alias. The meaning of the fields is displayed in the overview below.
| Field | Aliases | Description |
|---|---|---|
| left |
|
the distance between the left side of this component and the left side of the parent |
| top |
|
the distance between the top side of this component and the top side of the parent |
| right |
|
the distance between the right side of this component and the right side of the parent |
| bottom |
|
the distance between the bottom side of this component and the bottom side of the parent |
| rleft |
|
the distance between the left side of this component and the right side of the parent |
| rtop |
|
the distance between the top side of this component and the bottom side of the parent |
| rright |
|
the distance between the right side of this component and the left side of the parent |
| rbottom |
|
the distance between the bottom side of this component and the top side of the parent |
| width |
|
the width of the component |
| height |
|
the height of the component |
| name |
|
used to name a component |
When invoked (e.g. window resized) ALM will evaluate all expressions and resize/reposition the component accordingly. Missing fields can be derived from other fields. For example: When setting bottom and height, top is derived. When setting left and rright, width is derived. Ambiguous assignments will not be accepted. When a field is not set and can not be derived the original position is used with the preferred size of the component.
Expressions are arithmetic expressions in infix notation with operators: *, /, +, -, %. Brackets can be used for precedence. Additionally you can define references to other components and the component itself. References are of the form <componentName>.<field>. A componentName is the name of a sibling component and can be defined by setting the name field when adding the component. Two names have been predefined: parent is the containing component, this is the component itself.
The insets of containers are respected when setting positional fields.
This means that if you set the left inset to 10 pixels and define
left = 5; then the component will appear 15 pixels
from the left side of the parent. The insets are not
taken into account when referring to the size of the parent (e.g.
parent.width). This may lead to confusion when trying
to make a component the full width or height of the parent while there
are non-zero insets:
component.getInsets().set(10, 10, 10, 10);
panel.add(component,
"left = 0;"+
"width = parent.width;"); /* wrong, sticks out on the right side */
To correct the width we could substract the insets, but if they are
non constant this leads to very messy (string concatenation) code.
We recommend not to use parent.width in such situations, but rather to
use ALM's ability to 'stretch' a component by specifying a position
on both sides:
component.getInsets().set(10, 10, 10, 10);
panel.add(component,
"left = 0;"+
"right = 0;"); /* correct (full) width is calculated automagically */
The following examples are taken from a sample application (Web Start Demo - Full source).
Centering a component:
panel.add(title,
"x = 0.5 * parent.width - 0.5 * this.width;"+
"y = 20; ");
A close button:
JButton button = new JButton("X");
panel.add(button,
"top = 5; " +
"right = 20; ");
Dynamic width:
JLabel label = new JLabel("Name:");
panel.add(label,
"name = label; " +
"left = 15; " +
"top = 90; " +
"width = 0.2 * parent.width; ");
JTextField field = new JTextField("");
panel.add(nameField,
"name = nameField; " +
"left = label.rRight + 20; " +
"top = label.top; " +
"right = 20; ");
Marco Slot. A Computer Science student from the Netherlands currently doing a Master's in Parallel and Distributed Computer Systems at the Vrije Universiteit in Amsterdam.