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 2: Using XML to Express Constraints

XML has rapidly established itself as a convenient and flexible tool for configuring applications, and it's well supported in Java. It's not surprising that the first thing that came to mind when I was looking for a more compact and direct way to set up constraints in RelativeLayout was to use an XML file.



Whenever I need to work with XML, I start with JDOM, another great contribution from Jason Hunter and Brett McLaughlin. It provides a truly Java-centric (and massively convenient) interface for XML processing. You'll need to download the JDOM library and have it on your class path to use RelativeLayout's XML features.

If your Java SDK doesn't already include one, you'll also need an XML parser (there's one built in to Java 2 Standard Edition, versions 1.4 and later). I personally like the Apache XML Project's Xerces.

Moving the constraints to an XML file greatly simplifies our showAboutBox method. All that's left is to create the actual components, and then call XmlConstraintBuilder to add the constraints. The full source code for the revised example is in Example2.java. It starts out the same as before:

public static JFrame showAboutBox() {
   // Create the about box and assign it a RelativeLayout.
   final JFrame aboutBox = new JFrame("About Example 2");
   aboutBox.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
   RelativeLayout ourLayout = new RelativeLayout();
   aboutBox.getContentPane().setLayout(ourLayout);

Next comes the creation of the components to populate the frame. This code is no longer interspersed with constraint specifications.

// Add the application title, with a font size of twenty points.
JLabel title = new JLabel("Example 2");
title.setFont(title.getFont().deriveFont(20.0f));
aboutBox.getContentPane().add(title, "title");

// Add the version number.
aboutBox.getContentPane().add(new JLabel("Version 2.0"), "version");

// Add the date.
aboutBox.getContentPane().add(new JLabel("December, 2002"), "date");

// Create the scrolling details area, and fill it 
// with enough "information" to scroll.
JTextArea details = new JTextArea("This is where the details go...\n");
details.setEditable(false);
details.setLineWrap(true);
details.setWrapStyleWord(true);
for (int i = 1; i < 10; i++) {
   details.append("Filler line " + i + '\n');
}
aboutBox.getContentPane().add(new JScrollPane(details), "details");

// Finally, add the "OK" button.
JButton okButton = new JButton("OK");
aboutBox.getContentPane().add(okButton, "ok");

And it just takes a few lines to read in and apply the constraints. (A real program would respond more conscientiously to the potential parsing exception, though.)

// Set up the constraints for all components.
XmlConstraintBuilder builder = new XmlConstraintBuilder();
try {
   builder.addConstraints(new File("example2.xml"), ourLayout);
}
catch (Exception e) {
   e.printStackTrace();
}

Of course, all of the action in establishing constraints has been moved to the new configuration file, example2.xml, so let's look at that. It starts out with an ordinary XML preamble that identifies it as the proper type to be parsed by XmlConstraintBuilder:

<?xml version="1.0"?>
<!DOCTYPE  constraint-set
   PUBLIC "-//Brunch Boy Design//RelativeLayout Constraint Set DTD 1.0//EN"
   "http://dtd.brunchboy.com/RelativeLayout/constraint-set.dtd">

The top-level tag in the file identifies it as a constraint set, and it contains an entry for each component that needs to be constrained. The link with actual components to be constrained is established by the name attribute of the constrain tag. (There's an unfortunate clash of terms here: I'm talking about the XML attribute "name" of the tag "constrain," not one of the spatial attributes used to lay out a component. Hopefully, this won't cause too much confusion.) The value of name matches up with the name used when the component was added to the layout. The first component we constrain is the "title" label:

<constraint-set>
  <constrain name="title">
    <top>
       <toAttribute reference="_container" attribute="top" offset="10"/>
    </top>
    <horizontalCenter>
      <toAttribute reference="_container" attribute="horizontalCenter"/>
    </horizontalCenter>
  </constrain>

The structure of the XML is intended to read almost like English (well, to someone well-versed in the format and punctuation of XML, that is). Within the constrain tag there are a number of attribute tags to constrain particular attributes of the component; in this case, the top and horizontalCenter attributes. Case matters in XML, so it's important to use the "camel case" supported by XmlConstraintBuilder when creating these tags. If you've got a good code-completing editor, it may well be able to do this for you automatically by loading and understanding the XML DTD that defines a valid constraint-set document. The legal attribute tags are the same as the strings you can use with the getInstance() static factory method of the AttributeType class: left, top, right, bottom, width, height, horizontalCenter, and verticalCenter.

Within the top tag above, we specify the constraint for the top attribute of the component named title. The constraint consists of a toAttribute tag, which tells XmlConstraintBuilder to create an AttributeConstraint. The (XML) attributes of the toAttribute tag determine the parameters used to create that constraint:

<toAttribute reference="_container" attribute="top" offset="10"/>
  • reference specifies the name of the anchor component, the attribute of which we want to use as the basis for the constraint; in this case, _container. This is a special name that tells RelativeLayout we're interested in the attributes of the container in which layout is being performed. (It is the value of the DependencyManager.ROOT_NAME constant we used in the Java code of Example 1.)

  • attribute determines which of the anchor component's attributes we want to use to get the value of our constraint. In this example, we're looking at the top of the aboutBox frame. Capitalization here is the same as it is for the constraint tag itself.

  • offset is the integer offset to be added to the reference attribute in order to calculate the value for the attribute being constrained. This can be omitted completely in cases where you want the offset to be zero.

Putting this all together, we're constraining the top of the application title to be ten pixels below the top of aboutBox. The second constraint in this section can be read in the same way. It creates an AttributeConstraint that causes the horizontal center of the title to be at the horizontal center of the window.

The next stanza in the file creates the constraints for the version number, the top of which is eight pixels below the bottom of the title, and the left edge of which is five pixels from the left edge of aboutBox:

  <constrain name="version">
    <top>
      <toAttribute reference="title" attribute="bottom" offset="8"/>
    </top>
    <left>
      <toAttribute reference="_container" attribute="left" offset="5"/>
    </left>
  </constrain>

The constraints for the release date are quite similar, though this time we just say "use the same value for top as you use for the version number's top":

  <constrain name="date">
    <top>
      <toAttribute reference="version" attribute="top"/>
    </top>
    <right>
      <toAttribute reference="_container" attribute="right" offset="-5"/>
    </right>
  </constrain>

As in the all-Java example, there are more constraints for the "details" text area, because we want to take control of its width and height. We constrain the left, right, top, and bottom, using the same techniques as we have been above:

  <constrain name="details">
    <left>
      <toAttribute reference="version" attribute="left"/>
    </left>
    <right>
      <toAttribute reference="date" attribute="right"/>
    </right>
    <top>
      <toAttribute reference="version" attribute="bottom" offset="4"/>
    </top>
    <bottom>
      <toAttribute reference="ok" attribute="top" offset="-4"/>
    </bottom>
  </constrain>

Finally, the constraints for the "OK" button, which are our last, so we also end the constraint-set itself:

  <constrain name="ok">
    <horizontalCenter>
      <toAxis reference="_container" axis="horizontal" fraction="0.5"/>
    </horizontalCenter>
    <bottom>
      <toAttribute reference="_container" attribute="bottom" offset="-10"/>
    </bottom>
  </constrain>
</constraint-set>

Notice that in the horizontalCenter constraint we used a new toAxis tag. This is how you create an AxisConstraint using the XML format. It has three attributes:

  • reference specifies the anchor component along the axes of which we're going to pick a point for our constraint.
  • axis determines which of the axes we want to use; it must always be horizontal or vertical.
  • fraction determines where along that axis we want to position the constraint. 0.0 is the left (or top) of the component, and 1.0 is the right (or bottom) edge, as illustrated in Figure 3.

By constraining the button's horizontalCenter to 0.5 on the container's axis, we've simply centered it.

To compile and run this example, put Example2.java and example2.xml in the same directory, and make sure your class path contains JDOM and your XML parser. For example, if you've downloaded Xerces, you could use commands like the following :

javac -classpath jdom.jar:xerces.jar:RelativeLayout.jar Example2.java
java -classpath jdom.jar:xerces.jar:RelativeLayout.jar:. Example2

The window you get is essentially identical to the one from Example 1 (which was, after all, the goal). For the sake of variety, here's what it looks like in a different look and feel:


Example 2.

Pages: 1, 2, 3, 4

Next Pagearrow