Here is a simple example code about my question, you can run it with MigLayout.
public class TestFrame extends JFrame
{
public TestFrame()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(800, 500));
JTabbedPane pane = new JTabbedPane();
pane.addTab("TestFrame", new Panel1());
add(pane);
pack();
setVisible(true);
}
/**
* level-1 panel, also contains other components
* I just add incorrect panel(Panel2) here
*/
class Panel1 extends JPanel
{
public Panel1()
{
MigLayout layout = new MigLayout();
setLayout(layout);
add(new Panel2(), new CC().cell(0, 0).growX().pushX());
}
}
/**
* level-2 panel, also contains other components
* I just add incorrect panel(CheckBoxesPanel) here
*/
class Panel2 extends JPanel
{
public Panel2()
{
MigLayout layout = new MigLayout();
setLayout(layout);
add(new CheckBoxesPanel(), new CC().cell(0, 0).growX().pushX());
}
}
class CheckBoxesPanel extends JPanel
{
public CheckBoxesPanel()
{
WrapLayout layout = new WrapLayout(WrapLayout.LEADING);
setLayout(layout);
for (int i = 0; i < 20; i++)
{
add(new JCheckBox("JCB" + i));
}
}
}
public static void main(String[] args)
{
new TestFrame();
}
class WrapLayout extends FlowLayout
{
public WrapLayout()
{
super();
}
public WrapLayout(int align)
{
super(align);
}
public WrapLayout(int align, int hGap, int vGap)
{
super(align, hGap, vGap);
}
@Override
public Dimension preferredLayoutSize(Container target)
{
return layoutSize(target, true);
}
@Override
public Dimension minimumLayoutSize(Container target)
{
Dimension minimum = layoutSize(target, false);
minimum.width -= (getHgap() + 1);
return minimum;
}
private Dimension layoutSize(Container target, boolean preferred)
{
synchronized (target.getTreeLock())
{
Container container = target;
while (container.getSize().width == 0 && container.getParent() != null)
{
container = container.getParent();
}
int targetWidth = container.getSize().width;
if (targetWidth == 0)
{
return new Dimension();
}
int hGap = getHgap();
int vGap = getVgap();
Insets insets = target.getInsets();
int horizontalInsetsAndGap = insets.left + insets.right + (hGap * 2);
int maxWidth = targetWidth - horizontalInsetsAndGap;
Dimension dim = new Dimension(0, 0);
int rowWidth = 0;
int rowHeight = 0;
int count = target.getComponentCount();
for (int i = 0; i < count; i++)
{
Component m = target.getComponent(i);
if (m.isVisible())
{
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
if (rowWidth + d.width >= maxWidth)
{
addRow(dim, rowWidth, rowHeight);
rowWidth = 0;
rowHeight = 0;
}
if (rowWidth != 0)
{
rowWidth += hGap;
}
rowWidth += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
}
addRow(dim, rowWidth, rowHeight);
dim.width += horizontalInsetsAndGap;
dim.height += insets.top + insets.bottom + vGap * 2;
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
if (scrollPane != null && target.isValid())
{
dim.width -= (hGap + 1);
}
return dim;
}
}
private void addRow(Dimension dim, int rowWidth, int rowHeight)
{
dim.width = Math.max(dim.width, rowWidth);
if (dim.height > 0)
{
dim.height += getVgap();
}
dim.height += rowHeight;
}
}
}
I found that when I dragged slowly, the program wrapped correctly. There will be problems if dragging too fast.
Here are the GIFs of the correct and incorrect cases.
Correct wrap case
and
Incorrect wrap case
This seems to be related to the minimum size calculation, but I don’t know how to solve this problem.