CardLayout计算的最小尺寸不正确
CardLayout 无论显示的是哪个组件,都会计算最小宽度。请查看 java.awt.CardLayout#minimumLayoutSize。
复现步骤:
- 将窗口最大化。
- 将左侧组件切换为粉色组件(它更窄)。
- 还原窗口。
砰——分隔条被放置在一个怪异的位置,好像显示的是更宽的组件。
这个GUI模拟了我们应用中的实际布局。它包含一个拆分窗格,左侧的组件会在 CardLayout 的帮助下动态切换(在本场景中似乎最合适的布局)。容器有两个组件,它们的大小差异很大。分隔条的位置应该能够反映这种差异(同时仍然可用)。
如何规避这个问题?
最小可复现示例(MRE):
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.Arrays;
public class SplitPaneDemo {
static JSplitPane splitPane;
static JPanel leftPanel;
static CardLayout leftLayout;
public static void main(String[] args) {
Container mainPanel = createMainPanel();
JFrame frame = new JFrame("Split Pane Demo");
frame.setContentPane(mainPanel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static Container createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(createButtonPane(), BorderLayout.NORTH);
panel.add(createSplitPane());
return panel;
}
private static Component createButtonPane() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
panel.add(createButton());
return panel;
}
private static Component createButton() {
JButton button = new JButton("Swap left component");
button.addActionListener(SplitPaneDemo::swapLeftComponent);
return button;
}
private static void swapLeftComponent(ActionEvent e) {
leftLayout.next(leftPanel);
splitPane.setDividerLocation(getShowingPanel().getPreferredSize().width);
}
private static Component getShowingPanel() {
return Arrays.stream(leftPanel.getComponents())
.filter(Component::isShowing)
.findFirst()
.orElseThrow(null);
}
private static Container createSplitPane() {
splitPane = new JSplitPane();
splitPane.setLeftComponent(createLeftPanel());
splitPane.setRightComponent(createGreenPanel());
return splitPane;
}
private static Component createLeftPanel() {
leftPanel = new JPanel(leftLayout = new CardLayout());
leftPanel.setName("leftPanel");
leftPanel.add(createRedPanel(), "red");
leftPanel.add(createPinkPanel(), "pink");
return leftPanel;
}
private static Component createRedPanel() { // wider
Component panel = new JPanel();
Dimension size = new Dimension(500, 500);
panel.setPreferredSize(size);
panel.setMinimumSize(size); // crucial
panel.setBackground(Color.RED);
return panel;
}
private static Component createPinkPanel() { // narrower
Component panel = new JPanel();
panel.setPreferredSize(new Dimension(200, 500));
panel.setBackground(Color.PINK);
return panel;
}
private static Component createGreenPanel() {
Component panel = new JPanel();
panel.setPreferredSize(new Dimension(500, 500));
panel.setBackground(Color.GREEN);
return panel;
}
}


覆盖方法可行,但感觉不对劲。
此外,在实际应用中并不容易实现,因为我们使用Swing UI Designer GUI表单(本不应该这样做,但我们确实在用)。
leftPanel = new JPanel(leftLayout = new CardLayout() {
@Override // everything's the same...
public Dimension minimumLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
Insets insets = parent.getInsets();
int ncomponents = parent.getComponentCount();
int w = 0;
int h = 0;
for (int i = 0 ; i < ncomponents ; i++) {
Component comp = parent.getComponent(i);
if (!comp.isVisible()) continue; // ...except for this line
Dimension d = comp.getMinimumSize();
if (d.width > w) {
w = d.width;
}
if (d.height > h) {
h = d.height;
}
}
return new Dimension(insets.left + insets.right + w + getHgap()*2,
insets.top + insets.bottom + h + getVgap()*2);
}
}
});
Java 8。
解决方案
这不是我所期望的行为,但在调整大小时,似乎会丢失对 getPreferredSize() 的调用,可以通过向 frame 添加监听器来解决,具体如下:
frame.addComponentListener( new ComponentAdapter() {
@Override
public void componentResized( ComponentEvent e ) {
splitPane.setDividerLocation( getShowingPanel().getPreferredSize().width );
}
});
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。