Tracking another component’s size

January 21, 2009

flexible_space1

Sometimes it’s useful to have two disjoint components keep one of their dimensions in sync. For example, if you wanted to keep the space directly above the Source List in the screen shot above empty, a simple layout won’t suffice. In this case, there are two distinct components contained in different parents (the Source List is in a JSplitPane while the Unified Toolbar is contained in the “North” of the parent frame’s content pane).

What we want is the preferred size of a spacer JComponent to change based on the size of the Source List. This is actually easy to do with a ComponentListener. Here’s some code that creates a spacer that tracks the width or height of a given component. For an example of this technique in a real product, check out Panic’s Coda.

/**
 * A {@link JComponent} that tracks the width or height of another component.
 */
public class TrackingSpacer extends JComponent {

    private final JComponent fComponent;
    private final TrackingDimension fTrackingDimension;
    private final int fDelta;

    /**
     * Creates a spacer component that adjusts it's width or height to the 
     * given component.
     * @param componentToTrack the component to track the width or height of.
     * @param trackingDimension the dimension of the given compoonent to track.
     * @param delta the amount to add or subtract from the given components
     *        size (helps accomodate padding).
     */
    public TrackingSpacer(JComponent componentToTrack,
                          TrackingDimension trackingDimension,
                          int delta) {
        fComponent = componentToTrack;
        fTrackingDimension = trackingDimension;
        fDelta = delta;
        // listen for the given component to be resized.
        fComponent.addComponentListener(createComponentListner());
        // update the initial preferred size of the spacer.
        doTrackedComponentResized();
    }

    private void doTrackedComponentResized() {
        setPreferredSize(fTrackingDimension.createDimension(fComponent, fDelta));
        revalidate();
    }

    private ComponentListener createComponentListner() {
        return new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                doTrackedComponentResized();
            }
        };
    }

    /**
     * An enumeration representing the dimension of a component to track.
     */
    public enum TrackingDimension {
        WIDTH {
            // return a Dimension based on the given components width dimension.
            Dimension createDimension(JComponent component, int delta) {
                return new Dimension(component.getWidth() + delta, 1);
            }},
        HEIGHT {
            // return a Dimension based on the given components height dimension.
            Dimension createDimension(JComponent component, int delta) {
                return new Dimension(1, component.getHeight() + delta);
            }};

        abstract Dimension createDimension(JComponent component, int delta);
    }
}

4 Responses to “Tracking another component’s size”

  1. John Says:

    It can also be useful to override getPreferredSize for the tracking component to return the relevant dimension from the preferred size of the component being tracked.

  2. Ken Says:

    Hi John,

    True, in this case you could achieve the same behavior by overriding the spacer components getPreferredSize, though I avoid overriding methods whenever possible. Also note that using extension would only work on your own components, those that you could actually override the method on; you wouldn’t be able to apply the behavior to external components.

    -Ken


  3. […] content with one post, Ken blogs about allowing one component to track the size of another component. His post includes easy to follow code that I recommend other Swing developers take a look at for […]


  4. Or, you could use the Beans Binding Framework (JSR 295)
    https://beansbinding.dev.java.net/


Leave a comment