CardLayout计算最小尺寸异常导致JSplitPane分隔位置错误的问题及解决方案咨询
CardLayout计算最小尺寸异常导致JSplitPane分隔位置错误的问题及解决方案咨询
我遇到了一个CardLayout相关的布局问题:不知道为什么,CardLayout计算最小宽度时会忽略当前显示的组件,而是考虑所有组件的尺寸,具体可以查看java.awt.CardLayout#minimumLayoutSize方法的实现。
复现步骤
- 最大化窗口
- 将左侧组件切换为粉色的那个(它更窄)
- 恢复窗口到原来的大小
这时就能看到问题了——分隔条的位置变得很奇怪,就像左边还显示着那个更宽的组件一样。
这个GUI是模拟我们项目里的实际布局:我们用了一个拆分面板(JSplitPane),左侧组件通过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; } }
问题截图
- 全屏状态下的界面:

- 退出全屏后,分隔条位置异常的界面:

我自己尝试过重写CardLayout的minimumLayoutSize方法,只考虑当前可见的组件,这样确实能解决问题,但总感觉这种方式不太合理,而且在我们实际项目里也不好应用——因为我们用了Swing UI Designer可视化表单(虽然知道不该用,但目前确实是这么做的)。
重写的代码如下:
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。
备注:内容来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

