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

advertisement

AddThis Social Bookmark Button

JFC Swing: The SpringLayout Class
Pages: 1, 2, 3, 4

Manipulation Methods

Three manipulations are defined for springs. Recall that the calculated values are not actually hard-coded into the resulting springs. References to s1 and s2 are stored, so that if either spring changes, the resulting springs also change. (This is done with the help of a private inner class extension of Spring known as a "proxy spring.")



This method returns a new spring the properties of which represent the Math.max() of the respective properties of s1 and s2. In other words, the minimum property of the new spring is the max of s1.getMinimum() and s2.getMinimum().

public static Spring max(Spring s1, Spring s2)

This method returns a new spring, the properties of which represent the negative of the respective properties of s1. In other words, the minimum property of the new spring is -s1.getMinimum().

public static Spring minus(Spring s)

This method returns a new spring, the properties of which represent the sum of the respective properties of s1 and s2. In other words, the minimum property of the new spring is equal to s1.getMinimum() + s2.getMinimum().

public static Spring sum(Spring s1, Spring s2)

Other Operations

As mentioned earlier, you can combine the manipulation methods to create other operations. In particular, Spring.sum(s1, Spring.minus(s2)) returns the difference of the respective properties in s1 and s2. The Math.min() function can be mimicked using Spring.minus(Spring.max(Spring.minus(s1), Spring.minus(s2))). Try it on paper -- it really works!

Arranging Components

The combination of Spring math operations and constraints can make certain layouts easy to create (and easy to manipulate). That's "easy" in the "if you're designing a GUI builder" sense, of course. Figure 4 shows four buttons laid out in a vertical row with various Spring constraints holding them in place.


Figure 4. Vertically stacked buttons in a SpringLayout.

To show off more of the Spring constraint combinations, we varied the layout code for these buttons:

// We'll leave all buttons at their preferred widths and heights

// b1 gets placed at (10,10)
c.add(b1);
sl.getConstraints(b1).setX(offsetS);
sl.getConstraints(b1).setY(offsetS);

// b2 gets placed at (10, offset + b1.height + offset)
c.add(b2);
sl.getConstraints(b2).setX(offsetS);
sl.getConstraints(b2).setY(Spring.sum(Spring.sum(offsetS,
sl.getConstraints(b1).getHeight()), offsetS));

// b3 gets placed at (10, b2.south + offset)
c.add(b3);
sl.getConstraints(b3).setX(offsetS);
sl.getConstraints(b3).setY(Spring.sum(offsetS,
sl.getConstraint(SpringLayout.SOUTH, b2)));

// b4 gets placed at (b3.west, b3.south + offset)
c.add(b4);
sl.putConstraint(SpringLayout.WEST, b4, 0, SpringLayout.WEST, b3);
sl.putConstraint(SpringLayout.NORTH, b4, offsetS, SpringLayout.SOUTH, b3);

You could use any one of the techniques shown on all of the buttons, if you were so inclined. That said, there are a few consequences to the constraints we created in this example. For example, look at the y constraint of b2. It is simply the sum of two offsets and the height of b1. It is not dependent on the bottom edge of b1. It doesn't care where b1 is placed. The y constraints of b3 and b4, however, are dependent. If b2 moves down, so does b3 -- and if b3 moves down, so does b4.

One other fun constraint in this example is the left edge of b4. We tied it to the left edge of b3. If you change the x constraint of b3, b4 follows.

Custom Springs

Related Reading

Java Swing
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole

There are some things that cannot be duplicated using sum(), minus(), and max(). For those things, you can simply extend the Spring class. The compass navigation example in Figure 2 keeps the North and South buttons horizontally centered. (Of course, the East and West buttons are vertically centered.) To keep the buttons centered, even after the user resizes the frame, we need a new spring that returns the center of a parent spring. That can be built as we do in the FractionSpring class.

If you look a bit closer, this class can actually handle any multiplier value. The factory method half() produces the spring we need most often, but you can use the public constructor to supply an alternative. You could certainly write other factory methods for common values you find useful. Maybe a goldenMean() method is in your future?

One method you want to pay attention to is setValue(). In several derived springs (like our FractionSpring), the setValue() call does not make sense. Normally, we throw an UnsupportedOperationException to indicate that this required method from our abstract parent does not really apply; however, the special value UNSET can be used to help in a particular scenario: value caching.

If the current value of the spring comes from an expensive operation, you can cache that value. If your spring is bound to another spring, such as the border of your container, changing the size of the container causes a chain of UNSET values to be passed to dependent springs. You could watch for UNSET and recalculate your spring values only when you receive it. (Try uncommenting the println() in our setValue() method and rerun the example. You should see four UNSET calls each time you resize the frame -- one for each button.)

While it may take a few tries to get your brain around SpringLayout and spring math, you can accomplish some complex layouts with one container and a single, efficient layout manager.

Marc Loy is a trainer and media specialist in Cincinnati, OH. When he's not working with digital video and DVDs, he's programming in Java.


Return to ONJava.com.