tags:

views:

213

answers:

2

SpringLayout is claimed to be very powerful. I tried to implement what I thought was a fairly simple layout using SpringLayout and I'm failing badly.

Reduced to the minimum, I want 4 JButtons on my JFrame, side by side:

*=====================*
| +--+ +--+ +--+ +--+ |
| |b1| |b2| |b3| |b4| |
| +--+ +--+ +--+ +--+ |
*=====================*

I want all 4 to have the same size regardless of different texts.

I want the outermost ones (b1 and b4) to be a constant horizontal distance of 5 px from the borders of the container, and a 5 px border to the north and south of the buttons, which are all the same height.

I want the gaps between the buttons to be 5 px as well.

So far, so easy. I have more constraints:

  • When the frame is made wider (user/mouse), I want the gaps between the buttons (b1/b2, b2/b3, b3/b4) to widen, but not the buttons.

  • If the frame is made smaller, I want to gaps between the buttons to shrink down to 1 or maybe 0 before other stuff has to give way.

  • I expect a pack() on the frame to give me a window just the right size for the buttons with default 5px gaps and the 5px border around them.

I wrote the following fairly simple code, and results are horrible. The gaps don't shrink and expansion happens exclusively to the rightmost button (b4).

Now that I have custom buttons that take care of uniformly sizing themselves, I could meet these requirements fairly easily with GridBagLayout, and probably even more easily with MigLayout. That's not the answer I'm looking for. Specifically, my question is:

Can this layout be made to work correctly using SpringLayout? How?

The layout is even ignoring my buttons' getMaximumSize(). Am I doing something wrong or is SpringLayout bugged?

Thanks a lot.

public class SpringLayoutTest extends JFrame {

public SpringLayoutTest() {
  setLocation(100,100);
  setSize(400, 300);
  Container cp = getContentPane();
  SpringLayout layout = new SpringLayout();
  cp.setLayout(layout);

  SiblingButton b1, b2, b3, b4;
  cp.add(b1 = new SiblingButton("..."));
  cp.add(b2 = new SiblingButton("iii"));
  cp.add(b3 = new SiblingButton("xxx"));
  cp.add(b4 = new SiblingButton("WWW"));

  layout.putConstraint(NORTH, b1, 5, NORTH, cp);
  layout.putConstraint(SOUTH, b1, Spring.minus(Spring.constant(5)), SOUTH, cp);
  layout.putConstraint(NORTH, b2, 5, NORTH, cp);
  layout.putConstraint(SOUTH, b2, Spring.minus(Spring.constant(5)), SOUTH, cp);
  layout.putConstraint(NORTH, b3, 5, NORTH, cp);
  layout.putConstraint(SOUTH, b3, Spring.minus(Spring.constant(5)), SOUTH, cp);
  layout.putConstraint(NORTH, b4, 5, NORTH, cp);
  layout.putConstraint(SOUTH, b4, Spring.minus(Spring.constant(5)), SOUTH, cp);

  layout.putConstraint(WEST, b1, 5, WEST, cp);
  layout.putConstraint(WEST, b2, Spring.constant(1, 5, Integer.MAX_VALUE), EAST, b1);
  layout.putConstraint(WEST, b3, Spring.constant(1, 5, Integer.MAX_VALUE), EAST, b2);
  layout.putConstraint(WEST, b4, Spring.constant(1, 5, Integer.MAX_VALUE), EAST, b3);

  layout.putConstraint(EAST, b4, Spring.minus(Spring.constant(5)), EAST, cp);

  layout.putConstraint(WEST, cp, Spring.minus(Spring.constant(5)), WEST, b1);
}

public static void main(String[] args) {
  (new SpringLayoutTest()).setVisible(true);
}

SiblingButton is a pretty artless implementation. Please ignore its design faults, this is just for demo purposes.

class SiblingButton extends JButton {

static ArrayList<SiblingButton> siblings = new ArrayList<SiblingButton>();

public SiblingButton(String text) {
  super(text);
  siblings.add(this);
}

public Dimension getMaximumSize() {
  return getPreferredSize();
}

public Dimension getMinimumSize() {
   return getPreferredSize();
}

public Dimension getPreferredSize() {
  Dimension mx = new Dimension(0, 0);
  for (SiblingButton sb : siblings) {
     mx = new Dimension(Math.max(mx.width, sb.originalPreferredSize().width), 
           Math.max(mx.height, sb.originalPreferredSize().height));
  }
  return mx;
}

Dimension originalPreferredSize() {
  return super.getPreferredSize();
}

}


UPDATE / Conclusion

It's been 24 hours now, and the response has been overwhelmingly underwhelming. The lone response (thanks, Camickr!) doesn't even try to touch SpringLayout. I don't think this reflects poorly not on the SO community but on the utility of SpringLayout!

My impression is that SpringLayout is the red-headed stepchild of layout managers, useful in some ways but so little utilized that nobody has working experience with it, and nobody is bothering to report bugs to Sun.

For me, it turned out that GroupLayout does everything I needed, and allowed me to accomplish what I wanted exactly with a reasonable amount of coding. A welcome change from GridBagLayout, the steps I needed to take to achieve my intended layout were clear to me from the beginning and I just needed to sit down and write the code.

For anyone who cares, these are the characteristics of GroupLayout that made it so useful for me:

  • Layout is done more or less separately for the horizontal and vertical directions. Lots of flexibility and simpler code;
  • It's possible to nest grids without needing to introduce containers just to hold a layout;
  • It's possible to insert gaps with specified constraints;
  • It's possible to form arbitrary groups of components with shared sizing specs, i.e. all components have the same dimensions as the biggest in the group; and
  • It's possible to alter a component's sizing (e.g. maximumSize = preferredSize) without needing to subclass the component.
+1  A: 

You could use a BoxLayout:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class BoxExample extends JFrame
{
    public BoxExample()
    {
     Box box = Box.createHorizontalBox();
     box.setBorder( new EmptyBorder(5, 5, 5, 5) );
     Dimension size = new Dimension(100, 25);

     box.add( createButton("Button1", size) );
     box.add( createStrut() );
     box.add( createButton("Button2", size) );
     box.add( createStrut() );
     box.add( createButton("Button3", size) );
     box.add( createStrut() );
     box.add( createButton("Button4", size) );

     add( box );
    }

    private JButton createButton(String text, Dimension size)
    {
     JButton button = new JButton(text);
     button.setPreferredSize( size );
     button.setMinimumSize( size );
     button.setMaximumSize( size );
     return button;
    }

    private Component createStrut()
    {
     JComponent component = (JComponent)Box.createHorizontalStrut(5);
     component.setMinimumSize(new Dimension(0, 0));
     component.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
     return component;
    }

    public static void main(String[] args)
    {
     BoxExample frame = new BoxExample();
     frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
     frame.pack();
     frame.setLocationRelativeTo( null );
     frame.setVisible(true);
    }
}
camickr
Doesn't answer my question, but +1 for effort and a decent alternative!
Carl Smotricz
Also, accepted for being the only answer.
Carl Smotricz
A: 

You need to set the constraint of the container (the content pane of the frame) and more precisely the constraint east and south of it. Read the tutorial on this :
http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html
The SpringDemo3 explain it all

HeiffEire