【问题标题】:Have a JScrollPanel resize around a static JPanel让 JScrollPane 围绕静态 JPanel 调整大小
【发布时间】:2018-03-19 16:04:28
【问题描述】:

我正在为我正在创建的游戏制作编辑器,但我在实现可滚动的关卡概览时遇到了问题。概述应该有这样的布局:

Canvas 的大小应该始终为 1280x720,因此我想使用 JScrollPane 以便在 JFrame 变小时仍能查看整个画布。 现在为了实现这一点,我使用了 GridBagLayout 并在画布上设置了首选大小。问题是扩展 JFrame 最终将允许您将画布扩展到超出其首选大小。为了阻止这种情况,我将滚动窗格视口的布局设置为 FlowLayout 以防止它调整其持有的视图的大小。这似乎造成了两个问题:

1 : GridBagLayout 在展开到全屏时不再正确分配空间。

2:当您使 ScrollPane 小于其视口视图时,视图会被截断,滚动似乎也不起作用。

总结我的问题:

是否有另一种/更好的方法来实现此 UI,如果没有,我如何解决 gridbaglayout 在扩展到全屏并切断视口视图时无法正确分配空间的问题?

这里是我用于示例的代码:

public class Main extends JFrame {

public Main() {
    this.setSize(800, 400);
    this.setMinimumSize(new Dimension(400, 400));
    this.setLocationRelativeTo(null);

    JPanel optionsPanel = new JPanel();
    optionsPanel.setBackground(Color.red);

    JPanel scrollContentPane = new JPanel();
    scrollContentPane.setPreferredSize(new Dimension(700,700));
    scrollContentPane.setBorder(BorderFactory.createLineBorder(Color.red));

    JScrollPane scrollPane = new JScrollPane();
    scrollPane.getViewport().setLayout(new FlowLayout());
    scrollPane.getViewport().setView(scrollContentPane);
    scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_ALWAYS);


    JPanel contentPanel = new JPanel();
    contentPanel.setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();
    gc.fill = gc.BOTH;
    gc.weightx = 1.0;
    gc.weighty = 1.0;

    gc.gridx = 0;
    gc.gridy = 0;
    contentPanel.add(optionsPanel, gc);

    gc.gridx = 1;
    contentPanel.add(scrollPane, gc);

    this.setContentPane(contentPanel);
    this.setVisible(true);

}

public static void main(String[] args) {
    Main main = new Main();
}

}

这是我在程序中使用的实际代码:

    previewPanel = new PreviewPanel(this);
    controlPanel = new ControlPanel(this);

    centerPanel = new JPanel();
    centerPanel.setLayout(new GridBagLayout());

    GridBagConstraints c = new GridBagConstraints();
    c.fill = c.BOTH;
    c.weightx = 1;
    c.weighty = 1;

    // #HACK, fill grids with empty labels
    c.gridx = 0;
    c.gridy = 0;
    centerPanel.add(new JLabel(), c);

    c.gridx = 1;
    centerPanel.add(new JLabel(), c);

    c.gridx = 2;
    centerPanel.add(new JLabel(), c);

    c.gridx = 3;
    centerPanel.add(new JLabel(), c);

    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 1;

    centerPanel.add(controlPanel, c);

    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = 3;

    centerPanel.add(previewPanel, c);


    setLayout(new BorderLayout());
    add(centerPanel, BorderLayout.CENTER);
    add(menuBar, BorderLayout.NORTH);
}

【问题讨论】:

  • when expanded the gridbaglayout no longer distributes the space correctly. - 定义正确吗? GridBagLayout 首先为每个组件提供其首选大小。然后,如果有额外空间可用,该空间将根据您的权重进行划分,在本例中为 50/50。
  • 正确地,因为我只使用两个网格单元,所以我希望它只占用屏幕的一半。您可以在屏幕截图中看到,虽然它占用更多,我该如何防止呢?

标签: java swing


【解决方案1】:

边界切断;这是因为JScrollPane 没有将边框识别为组件的一部分。因此,当您一直滚动到边缘时,您将看到组件的第一个非边框像素。为了解决这个问题,而不是在视口上设置布局,创建另一个面板,将画布添加到这个新面板,并将这个新面板设置为滚动窗格中的视口。

JPanel scrollContentPane = new JPanel();
// to set the size properly see later
JPanel scrollContentPaneViewport = new JPanel(new FlowLayout());
scrollContentPaneViewport.add(scrollContentPane);
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setView(scrollContentPaneViewport);

要限制画布的大小 (scrollContentPane),您不应设置大小提示 set[minimum|preferred|maximum]size (reason is here),而是应将面板子类化并覆盖 get[minimum|preferred|maximum]size 方法,覆盖您需要的方法,但如果您想要使面板始终固定大小,您应该覆盖所有三个。

JPanel scrollContentPane = new JPanel() {
    // this is important never return this dimension always make a copy
    private final Dimension d = new Dimension(700, 700);

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(d);
    }
    @Override
    public Dimension getMinimumSize() {
        return new Dimension(d);
    }
    @Override
    public Dimension getMaximumSize() {
        return new Dimension(d);
    }
};

要确保 optionsPanel 和 scrollPane 始终等分,请使用 GridLayout 而不是 GridBagLayout

编辑:GridBagLayout组件相对分布

您应该向布局提供有关如何处理额外空间的信息。这是通过设置rowWeightscolumnWeights 字段来完成的。

JPanel contentPanel = new JPanel();
GridBagLayout gbl_contentPanel = new GridBagLayout();
// This tells the layout that first row should take all available space (there is only one row)
gbl_contentPanel.rowWeights = new double[]{1.0};
// This tells the layout that second column should be 3 times bigger than first column (3 is 3 times bigger than 1)
gbl_contentPanel.columnWeights = new double[]{1.0, 3.0};
contentPanel.setLayout(gbl_contentPanel);

这应该告诉GridBagLayout 如何分配额外空间,但它仍然不起作用。

这是因为GridBagLayout 是尊重从组件返回的首选大小的布局之一。您添加的两个组件正在“告诉”布局它们的最小最大值和首选大小,并且布局在计算组件应该获得多少空间时会考虑到它。

为了解决您应该在两个组件中覆盖 getPreferredSize() 以返回相等的尺寸。 (我喜欢返回 0, 0)

JPanel optionsPanel = new JPanel() {
    @Override
    public Dimension getPreferredSize() {
        return new Dimension();
    }
};

【讨论】:

  • 这很好地解决了截止边界的问题。唯一剩下的问题是,当扩展 gridbaglayout 时,不再正确分配空间。
  • 你想如何分配空间?一半一半?一半用于选项,一半用于滚动窗格?在这种情况下,简单的GridLayout 会更好。
  • 回想起来,我选择了一个坏例子。在我的实际 UI 中,空间以四分之一分布,这意味着“画布”面板占据了 3/4 的空间,选项占据了 1/4。问题仍然存在,当您将框架展开过多时,选项部分会变得模糊。
  • 这改变了事情,我知道是什么导致了问题,我将编辑答案。
  • 您不应该仅仅为了添加列而添加组件,使用rowWeights / rowHeightscolumnWeights / columnWidths 来定义布局中的行数和列数。如果您只需要并排的两个组件,则不需要设置gridwidth,因为您应该只使用 2 列。设置columnWeights 告诉布局如何划分空间。无论如何,这个答案太长了,你应该完成你所拥有的并提出一个关于在GridBagLayout中划分空间的新问题。
【解决方案2】:

不要玩视口的布局管理器

如果您想控制添加到视口的面板的大小,请将您的面板添加到包装面板。比如:

JScrollPane scrollPane = new JScrollPane();
//scrollPane.getViewport().setLayout(new FlowLayout());
//scrollPane.getViewport().setView(scrollContentPane);
JPanel wrapper = new JPanel();
wrapper.add( scrollContentPane );
scrollPane.getViewport().setView(wrapper);

【讨论】:

    猜你喜欢
    • 2014-05-22
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多