Java 让JScrollPanel围绕静态JPanel调整大小

Java 让JScrollPanel围绕静态JPanel调整大小,java,swing,Java,Swing,我正在为我正在创建的游戏制作一个编辑器,但在实现可滚动的关卡概览时遇到了问题。概览应具有以下布局: 画布的大小应始终为1280x720,因此我希望使用JScrollPane,以便在JFrame变小时仍能查看整个画布。 现在为了实现这一点,我使用了GridBagLayout并在画布上设置了一个首选大小。问题是,扩展JFrame最终将允许您将画布扩展到其首选大小之外。要停止此操作,我将scrollpanes视口的布局设置为FlowLayout,以防止其调整视图大小。这似乎造成了两个问题: 1:Gr

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

画布的大小应始终为1280x720,因此我希望使用JScrollPane,以便在JFrame变小时仍能查看整个画布。 现在为了实现这一点,我使用了GridBagLayout并在画布上设置了一个首选大小。问题是,扩展JFrame最终将允许您将画布扩展到其首选大小之外。要停止此操作,我将scrollpanes视口的布局设置为FlowLayout,以防止其调整视图大小。这似乎造成了两个问题:

1:GridBagLayout在扩展为全屏幕时不再正确分配空间

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

总结我的问题:

是否有其他/更好的方法来实现此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);
}

不要使用视口的布局管理器

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

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

不要使用视口的布局管理器

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

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

边界被切断;这是因为
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)的大小,您不应该设置大小提示
设置[minimum | preferred | maximum]size
(),而应该对面板进行子类化,并覆盖
获取[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);
    }
};
要确保选项面板和滚动窗格始终等分,请使用
GridLayout
而不是
gridbagloayout

编辑:
GridBagLayout
组件相对分布

您应该为布局提供有关如何处理额外空间的信息。这是通过设置
行权重
列权重
字段来实现的

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)


边界被切断;这是因为
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)的大小,您不应该设置大小提示
设置[minimum | preferred | maximum]size
(),而应该对面板进行子类化,并覆盖
获取[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);
    }
};
要确保选项面板和滚动窗格始终等分,请使用
GridLayout
而不是
gridbagloayout

编辑:
GridBagLayout
组件相对分布

您应该为布局提供有关如何处理额外空间的信息。这是通过设置
行权重
列权重
字段来实现的

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)


这很好地解决了截止边界的问题。唯一剩下的问题是,当gridbaglayout展开时,它不再正确地分配空间。您希望如何分配空间?一半一半?一半用于选项,一半用于滚动窗格?在这种情况下,简单的
GridLayout
会更好。回想起来,我选择了一个不好的例子。在我的实际用户界面中,空间分布在四分之四,这意味着“画布”面板占据了空间的三分之四,选项占了四分之一。问题仍然是一样的,当你太多地扩展框架时,选项部分会被弄脏。这改变了一切,我知道是什么导致了问题,我将编辑答案。你不应该仅仅为了添加列而添加组件,请使用
行权重
/
行权重