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

advertisement

AddThis Social Bookmark Button

RelativeLayout, a Constraint-Based Layout Manager
Pages: 1, 2, 3, 4

Example 3: Using Multiple Anchors with AttributeConstraint

This last example won't be discussed in nearly as much detail as the others, but it illustrates a useful feature that might otherwise be missed. AttributeConstraint allows you to specify the names of more than one component to use as an "anchor" for the constraint, as a comma-delimited list. When you do this, it calculates a bounding box, the smallest rectangle that contains all of the components in the list, and gives you access to the attributes of this bounding box.



To show a situation in which this can be useful, imagine you're creating a styled text editor, and you want to create a formatting dialog with a bunch of check boxes representing the different styles that can be combined, and a text area showing a sample of what the resulting text would look like. With more than a few styles, you might need multiple columns of text boxes. How can you get them to line up nicely? You could probably guess which of the checkboxes is longest, and use it as the anchor. But even if you guess right, the layout will break if someone later changes the text (for example, to localize the program for a different language).

AttributeConstraint's support for multiple anchors lets you cope with this easily. By using the bounding boxes around an entire column of choices to build the constraint for the next column, you don't have to worry about which item is the longest. This also lets you constrain the position of the sample text. Here's what the resulting window looks like:


Example 3.

Apart from creating a different set of Swing components, the Java source for this example is very similar to Example 2. Many of the XML constraint definitions are as well, but there are a couple worth highlighting. Here's how the "strikethrough" checkbox is positioned:

<constrain name="s">
  <top>
    <toAttribute reference="bold" attribute="top"/>
  </top>
  <left>
    <toAttribute reference="bold,italic,underline" attribute="right" offset="10"/>
  </left>
</constrain>

The top is aligned with the "bold" checkbox in the same way we've been doing it all along, but the left is constrained to fall ten pixels past the right of the bounding box around all three of the "bold", "italic," and "underline" checkboxes. Since "underline" is the widest, and they're all lined up along their left edges, this positions "strikethrough" just to the right of "underline." The sample text area uses the same approach, but leaves a larger gap of twenty pixels between its left edge and the second column of styles:

<constrain name="sample">
  <top>
    <toAttribute reference="caption" attribute="bottom" offset="4"/>
  </top>
  <bottom>
    <toAttribute reference="apply" attribute="top" offset="-10"/>
  </bottom>
  <left>
    <toAttribute reference="s,tt,em,strong" attribute="right" offset="20"/>
  </left>
  <right>
    <toAttribute reference="_container" attribute="right" offset="-10"/>
  </right>
</constrain>

This also nestles it below the "Sample Text:" caption and above the "Apply" button, and puts its right side ten pixels from the edge of the window.

The Java source for this example is Example3.java and the constraints are defined in example3.xml. Running it requires the same class path as Example 2.

That's all there is to using RelativeLayout. These examples provide an introduction to some of the practical ways it can be applied. I hope that reading them has brought to mind an instance or two where it would have simplified building an interface. Although there are many more useful ways to combine constraints and anchors than we've explored here, RelativeLayout can't handle every imaginable situation by itself. You'll still sometimes need to nest containers and enlist the help of other layout managers, though perhaps less often than before.

Should you enjoy experimenting beyond the examples, there are some pretty impractical arrangements you can set up, too. Nothing prevents you from constraining an attribute to a bizarrely unrelated one, even on a different axis (for example, setting the left of one component based on the height of another). Such arrangements can behave in very odd ways. Of course, unless you're trying to set up puzzles for someone along the lines of "resize this window a few times and see if you can identify the constraints I used," you're unlikely to do such a thing.

If you read on through the discussion of how RelativeLayout actually works, you'll also see that it can support completely new kinds of constraints, too. If you can think up a useful addition to AttributeConstraint and AxisConstraint, I'd love to hear about it. Another exercise that's been "left for the reader" is to add multiple-anchor support to AxisConstraint. If you do that, it would make sense to move the supporting code to an abstract skeleton that is a new ancestor to both AttributeConstraint and AxisConstraint (and any future constraint that might want to support multiple anchors). But I'm getting ahead of myself -- if you're interested in this sort of discussion, be sure to read the "under the hood" section.

Origins of RelativeLayout

Before digging in to the source code and explaining how everything works, I'd like to pause to acknowledge the books and people that were most instrumental in enabling and motivating me to create this tool.

The key concepts were first introduced (to me, anyway) by the "Custom Interdependent Layout Manager" detailed in Philip Heller and Simon Roberts' Java 2 Developer's Handbook (SYBEX). I found their FormLayout to be a terrific idea, and immediately wanted to go further with it. Their book predated the availability of convenient XML tools in Java, which forced a much more awkward and cryptic configuration mechanism on them. Although the code worked just fine in JDK 1.1, it didn't take advantage of the more modern Collections framework, nor the kind of refined object-oriented API that can be achieved in a mature Java program. In fact, with all due respect to clean and working code, many pieces seemed to have been ported just far enough from C to pass muster with the Java compiler.

I felt I owed it to the great ideas embodied by the algorithms to give them a new expression, writing a similar layout manager while thinking "What Would Joshua Do?" Joshua Bloch is, of course, the author of the truly indispensable Effective Java Programming Language Guide (Addison Wesley Professional), as well as the aforementioned Collections classes. RelativeLayout makes extensive use of such useful Java idioms as the type-safe enumeration, interfaces, and immutability. It's designed to form an extensible API so that you can come up with new constraint types of your own and plug them right in. These features will be illustrated in more depth in the next part of this series.

I hope Philip and Simon like the younger sibling inspired by their own creation. (And, actually, Philip had another impact on this project: his two-day Java University course was a big help developing my expertise in the language when I finally started using Java professionally in the spring of 2000.)

Finally, I have to thank Marc Loy again for convincing me that I should start writing for a larger audience than my project teams at work. Getting me involved in the revision of Java Swing is what pushed me over the edge into actually creating RelativeLayout, after thinking about it for almost a year.

If you'd like to look at how the internals of the layout manager actually work, my next article will examine its design and source code.

James Elliott is a senior software engineer at Singlewire Software, with fifteen years' professional experience as a systems developer.


Return to ONJava.com.