ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button
Java Swing, 2nd Edition

RelativeLayout, a Constraint-Based Layout Manager

by James Elliott, coauthor of Java Swing, 2nd Edition


If you pick up the second edition of Java Swing (literally, you know, heft the sucker), you might find it hard to believe we left anything out. In fact, for this revision, we tried to cut back significantly on anything that bordered on esoterica, to leave more space for useful explanations and practical examples.

Even so, there was enough new material to cover that we couldn't fit in everything we wanted. This was especially true for topics peripheral to the main thrust of the book, no matter how interesting we found them. Our editor came up with a nice way out, of which this article is the first installment: we're publishing some of the self-contained supplementary examples as articles on ONJava.com.

Layout managers fall firmly into the periphery when talking about Swing. They're something you use all the time with Swing containers, but they predate Swing and you can use them just as well with an AWT application. Still, layout managers play such a fundamental role in arranging the pieces of your user interface that you need to develop a good understanding of at least a couple of them so that you can work effectively. And they hold the promise of enabling your application to look polished as it moves from platform to platform, gracefully adapting to new font metrics and component shapes.

Source Code

Download the source code for this article. This zip file contains the following:
Example1.java, Example2.java, example2.xml, Example3.java, example3.xml and the RelativeLayout.jar library.

Alas, the experience of most people is just the opposite. They find the available layout managers either too simple-minded to achieve the results they have in mind, or too confusing to really understand, so they try to find example code that's "close," tinker with some parameters in a form of voodoo, and pray that what finally seems to look OK on their system will be good enough in the wild. (Yes, I know there are people who have long mastered the Zen of GridBagLayout and can refactor an interface sketch into a minimal set of nested BorderLayouts in the blink of an eye, but the rest of us could still use a little help and some more alternatives.)

To that end, Marc Loy has written an article about SpringLayout, a new layout manager in the 1.4 SDK that will dovetail nicely with visual GUI design environments. This article presents my own layout manager, called RelativeLayout.

RelativeLayout is aimed squarely at mere mortals who are trying to translate their interface ideas into portable and resizable Java implementations. I put it together both so that I could use it myself, and to act as an example of how to write a complete layout manager. You're free to use it in either capacity: download the compiled jar and read enough of the article to find out how to use RelativeLayout in your own projects, or delve into the source code and see how it works and why.

Using RelativeLayout

The basic idea behind RelativeLayout is to let you specify the relationships between elements in your interface, and let the layout manager sort out the details. To demonstrate how it works, let's put together an "About Box" for an imaginary application. These things often start as napkin sketches (or design documents from the creative department that might as well be) with notes about how pieces of the interface relate to each other. Something like this:

Figure 1. Typical UI sketch.

These sorts of relationships are exactly what RelativeLayout is designed to support. We'll look at two different ways of setting them up. The first approach is to directly create and add the constraints through Java code, as illustrated by Example1.java, which we'll examine first. If you want to see how it behaves before reading about how it works, download RelativeLayout.jar to the same directory, and compile and run the example as follows:

javac -classpath RelativeLayout.jar Example1.java
java -classpath RelativeLayout.jar:. Example1

Try resizing the window a couple of times to see how the constraints interact with each other. Before delving into the source code of the example, it's worth introducing the basic concepts underlying RelativeLayout.

Attributes and Constraints

RelativeLayout positions components using eight attribute types that it can examine and manipulate. The figure below shows all of the attributes as they apply to a large label. There are four attributes that apply along the horizontal axis, and which are shown in green: Left, Horizontal Center, Right, and Width. The rest of the attributes, shown in blue, apply to the vertical axis: Top, Vertical Center, Bottom, and Height.

Figure 2. Attribute types.

When a component is laid out, RelativeLayout establishes values for all eight attributes using information about how you want things arranged. The attributes are redundant -- of the four attributes that apply to a given axis, if you know any two, you can calculate the other two. So when you're telling RelativeLayout how to position a component, you need to assign values to any two of the attributes on each axis. And since components have built-in ideas about their own preferred widths and heights, you can get away with supplying even less information.

To lay out a component with RelativeLayout, you generally need to specify only one attribute for each axis, and leave the width and height at their natural values. If you do specify width or height, you still need to constrain one other attribute on that axis, or the component will be under-constrained. If you try to specify more than two components on the same axis, it will be over-constrained. Either problem will cause layout to fail. This is a design choice; some layout managers try to make their best guess about what to do in similar situations, but that makes them harder to predict and understand.

So how do you pin down the attributes of a component, in order to lay it out? Given the discussion above, it may not be too surprising that you add constraints to the component. Each constraint applies to a single attribute of the component, and determines its value. What makes the layout relative is that the constraints themselves get their values from other components, or the container in which layout is being performed. You'll most often use an AttributeConstraint, which lets you base one attribute on another. You tell it to start with the value of any attribute of an "anchor" component, add an integer offset (which may be zero), and assign the result to the attribute you want to constrain.

The constraints described in Figure 1 can be represented quite naturally this way. For example:

  • The top attribute of "Version" is equal to the bottom attribute of "Application Name" plus 8.
  • The horizontal center attribute of "Application Name" is equal to the horizontal center of the container.
  • The scrolling text field's right attribute is equal to the right of "Release Date".

And so on. The process of setting up RelativeLayout is simply a matter of expressing these constraints in Java by creating objects that represent them. You don't need to worry about the order in which you add constraints. When it's time to actually perform the layout, RelativeLayout will analyze all of the constraints it's been given and make sure that there are enough (but not too many), that they're consistent, and that there are no circular dependencies. Let's examine the relevant code in the example program to make this concrete.

Pages: 1, 2, 3, 4

Next Pagearrow