I finally got the behavior I want for vertically stacking components that have a preferred height that changes with time. But I needed to use MigLayout.
Is there a way to do this w/o MigLayout? (It’s for a library and I don’t want to force the dependency unless I have to)
Here’s the behavior I’m looking for (which my test program achieves):

In vertical order, there’s a resize button, “empty space” (well, a JLabel marked as such), a red rectangle, and a green square. The resize button has fixed height. The red square has a random size that can change at arbitrary times. The green square sets its preferred height to match its width, and I want to expand its width to fill the container. The empty space expands horizontally and vertically to fill the remaining space in the container.
What would work instead of MigLayout?
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class AutoResizeDemo extends JPanel
{
static private class ResizingPanel extends JPanel
{
final private Color color;
private Dimension dpref = new Dimension(100,100);
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
g.setColor(this.color);
g.fillRect(0, 0, w, h);
g.setColor(Color.BLACK);
g.drawRect(0, 0, w-1, h-1);
String s = this.dpref.width+"x"+this.dpref.height;
FontMetrics fm = g.getFontMetrics();
g.drawString(s, 0, fm.getHeight());
}
public ResizingPanel(Color color, boolean isSquare)
{
this.color = color;
if (isSquare)
{
addComponentListener(new ComponentAdapter() {
@Override public void componentResized(ComponentEvent e) {
doResize(getWidth(), getWidth());
}
});
}
}
@Override public Dimension getPreferredSize() {
return this.dpref;
}
public void doResize(int w, int h)
{
this.dpref = new Dimension(w, h);
revalidate();
}
}
public AutoResizeDemo()
{
super(new MigLayout("","[grow]",""));
setPreferredSize(new Dimension(200, 800));
final ResizingPanel resizingPanelRandom = new ResizingPanel(Color.RED, false);
ResizingPanel resizingPanelSquare = new ResizingPanel(Color.GREEN, true);
JPanel buttonPanel = new JPanel(new FlowLayout());
final Random rand = new Random();
addButton(buttonPanel, "resize",new Runnable() {
@Override public void run() {
resizingPanelRandom.doResize(
rand.nextInt(100)+100,
rand.nextInt(100)+100
);
}
});
add(buttonPanel, "wrap");
JLabel spaceLabel = new JLabel("empty space");
spaceLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(spaceLabel, "push, grow, wrap");
add(resizingPanelRandom, "wrap");
add(resizingPanelSquare,"pushx, growx, wrap");
}
private void addButton(JPanel panel, String title, final Runnable r) {
JButton button = new JButton(title);
button.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
r.run();
}
});
panel.add(button);
}
public static void main(String[] args) {
JFrame frame = new JFrame(AutoResizeDemo.class.getSimpleName());
frame.setContentPane(new AutoResizeDemo());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
You can solve this using
SpringLayoutby wiring all your compenents together and to the edges of their container.Button Panel
left and top of the button panel to left and top of the container panel
Green Panel
left, right and bottom to the left, right and bottom of the container panel
Red Panel
left to left of container panel and bottom to top of the green panel
Space Label
top to south of the button panel, left and right to left and right of the container panel, bottom to top of the red panel
Edit: I love SpringLayout, there’s nothing it can’t do.