A LayoutManager Sample


For one application, I wanted a pair of buttons on the bottom left and "OK" and "Cancel" buttons on the bottom right. Thinking that this might be a recurring requirement, I decided to write a LayoutManager to provide this service. To keep things simple, I decided to have a Row object (a lightly extended JPanel) that would be laid out by a RowLayout.

Being a fan of the BorderLayout, I chose a similar API design. Figure 1 shows a JFrame with a BorderLayout, a button in the center and a Row in the south.

Screen shot of RowLayout with buttons on left and right
Figure 1
This is the code that creates and populates the Row:

    Row row = new Row();
    row.add( new JButton("Up"), RowLayout.LEFT );
    row.add( new JButton("Down"), RowLayout.LEFT );
    row.add( new JButton("Cancel"), RowLayout.RIGHT );
    row.add( new JButton("OK"), RowLayout.RIGHT );

As you see, the RowLayout is even simpler to use than a BorderLayout, but what price for this simplicity? Did it take a Herculean coding effort? Not at all. Let's begin by looking at the actual layout code.

The Layout Code

As explained in my LayoutManager Interface article, the method to implement is layoutContainer(). That method gets the array of components from the parent container, initializes a left and right pointer and then loops over the components, calling a private layout() method.

public void layoutContainer(Container c) {

    Component[] comps = c.getComponents();
    left = 0;
    right = c.getWidth();

    for ( int i = 0; i < comps.length; i++ ) {
        layout( comps[i], c );
    }

} // end of layoutContainer()
The private layout method isn't any more complex. It just:
  1. Casts the parent to Row
  2. Sets the component's size to its preferred size
  3. Asks the row if the component goes on the left
  4. Sets the location (left or right)
  5. Updates the left or right pointer
This is the code:

    private void layout( Component comp, Container row ) {

        Row r = (Row) row;
        comp.setSize( comp.getPreferredSize() );

        if ( r.isLeft( comp ) ) {
            comp.setLocation( left, 0 );
            left += comp.getWidth();
        }
        else {
            right -= comp.getWidth();
            comp.setLocation( right, 0 );
        }

    } // end of layout( Component, Container )
Excluding some administrative details (a toString() method and stubbing out the other four interface methods) you've seen the entire RowLayout LayoutManager. It couldn't be much simpler.

The Row Object

The Row extends a JPanel, but only slightly. There are just three working methods (again, skipping some administrative details). The first is the add() method. It actually adds components and it puts the components into a hashtable with a LEFT or RIGHT object, so that it can respond to the RowLayout's isLeft() query.

    public Component add( Component c, int where ) {
        if ( where == RowLayout.LEFT ) {
            super.add( c );
            table.put( c, LEFT );
        }
        else if ( where == RowLayout.RIGHT ) {
            super.add( c );
            table.put( c, RIGHT );
        }
        else error( "Incorrect location" + ADD_MSG );

        return c;

    } // end of add( Component, int )
The hashtable makes the isLeft() method trivial.

    public boolean isLeft( Component c )
    { return table.get( c ) == LEFT; }
Finally, there is a getMinimumSize() method that could be used to prevent the user from resizing to a size that won't fit all the components.

    public Dimension getMinimumSize() {

        int width = 0, height = 0;
        Component[] comps = getComponents();

        for ( int i = 0; i < comps.length; i++ ) {

            Component c = comps[i];
            width += c.getPreferredSize().width;
            height = Math.max
                ( height, c.getPreferredSize().height );
        }

        return new Dimension( width, height );

    } // end of getMinimumSize()
The administrative details are a toString() method and several flavors of add() and remove() that throw an Error with a "please use the correct method" message to the programmer. (The add() flavors that throw an Error are not an exhaustive list - just the ones that are commonly used to add components to a container.)

Summary

Creating a custom LayoutManager is not a big job and it doesn't require advanced coding skills. Next time you're thinking about absolute positioning when none of Sun's LayoutManagers solve your problem, think about writing your own LayoutManager.

© 2005 by Martin Rinehart


java consultant home page icon Back to Articles