I'm experimenting with a custom LayoutManager and I don't understand the subtleties of using it within a component that itself has an upper-level layout manager.
Below is a test program that makes two frames with pair of JPanels. Each of the two JPanels has a thin black border and uses my WeirdGridLayout to force its child components into squares in a grid, with the JPanel height being computed from the width. Both JPanels are in another JPanel with a thin red border that uses BorderLayout.
In one frame, the JPanels with WeirdGridLayout are arranged EAST and WEST, the other arranged NORTH and SOUTH.
The problem is that in the north/south case, if I change the width/height of the frame, the two JPanels with WeirdGridLayout have the right size, but not the right position (they either have a gap or overlap vertically).
In the east/west case, it just ends up wrong.
What I have to do to get my layout manager to play well with outer layout managers?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.LayoutManager2;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
*/
public class WeirdGridLayout implements LayoutManager2
{
static final private int GRIDGAP = 10;
static final private int COMPONENT_SIZE = 30;
static final private int GRIDSPACING = COMPONENT_SIZE + GRIDGAP;
final private List<Component> components
= new ArrayList<Component>();
@Override public void addLayoutComponent(Component comp, Object constraints) {
this.components.add(comp);
}
@Override public void addLayoutComponent(String name, Component comp) {
this.components.add(comp);
}
@Override public void removeLayoutComponent(Component comp) {
this.components.remove(comp);
}
@Override public float getLayoutAlignmentX(Container target) {
return Component.LEFT_ALIGNMENT;
}
@Override public float getLayoutAlignmentY(Container target) {
return Component.TOP_ALIGNMENT;
}
@Override public void invalidateLayout(Container target) {}
@Override public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
@Override public Dimension minimumLayoutSize(Container parent) {
return new Dimension(0,0);
}
@Override public void layoutContainer(Container parent) {
int x = GRIDGAP;
int y = GRIDGAP;
Dimension d = preferredLayoutSize(parent);
parent.setSize(d);
for (Component component : this.components)
{
component.setBounds(x, y, COMPONENT_SIZE, COMPONENT_SIZE);
x += GRIDSPACING;
if (x >= d.getWidth())
{
x = GRIDGAP;
y += GRIDSPACING;
}
}
}
@Override public Dimension preferredLayoutSize(Container parent) {
// how many blocks wide can we fit?
int n = this.components.size();
int nblockwidth = (parent.getWidth() - GRIDGAP) / GRIDSPACING;
int nblockheight = (nblockwidth == 0) ? 0
: ((n-1)/nblockwidth) + 1;
return new Dimension(
nblockwidth*GRIDSPACING+GRIDGAP,
nblockheight*GRIDSPACING+GRIDGAP);
}
/* ---- test methods ---- */
static public class ColorPanel extends JPanel {
final private Color color;
final private String label;
public ColorPanel(String label, Color color) {
this.label = label;
this.color = color;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(this.color);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.WHITE);
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(this.label);
g.drawString(this.label, (getWidth()-w)/2,
(getHeight()+fm.getAscent())/2);
}
}
public static void main(String[] args) {
showFrame(true);
showFrame(false);
}
private static void showFrame(boolean eastWest) {
JFrame frame = new JFrame("WeirdGridLayout test: eastWest="+eastWest);
JPanel framePanel = new JPanel(new BorderLayout());
framePanel.setPreferredSize(new Dimension(400,200));
JPanel panel[] = new JPanel[2];
for (int i = 0; i < 2; ++i)
{
panel[i] = new JPanel(new WeirdGridLayout());
panel[i].setBorder(BorderFactory.createLineBorder(Color.BLACK));
final Random r = new Random();
for (int j = 0; j < 24; ++j)
{
Color c = new Color(
r.nextFloat(),
r.nextFloat(),
r.nextFloat());
JPanel subpanel = new ColorPanel(Integer.toString(j), c);
panel[i].add(subpanel);
}
}
framePanel.add(new JButton("test"), BorderLayout.NORTH);
JPanel bottomPanel = new JPanel(new BorderLayout());
framePanel.add(bottomPanel, BorderLayout.SOUTH);
if (eastWest)
{
bottomPanel.add(panel[0], BorderLayout.WEST);
bottomPanel.add(panel[1], BorderLayout.EAST);
}
else
{
bottomPanel.add(panel[0], BorderLayout.NORTH);
bottomPanel.add(panel[1], BorderLayout.SOUTH);
}
bottomPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
frame.setContentPane(framePanel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}