【问题标题】:GridBagLayout: how to fill vertically using preferred width horizontallyGridBagLayout:如何使用首选宽度水平垂直填充
【发布时间】:2017-10-23 09:36:15
【问题描述】:

我在尝试在JPanel 中垂直填充JScrollPane(其中包含JTable)时遇到问题:面板不应水平填充,而JScrollPane 应仅采用“必要”空间。

我正在使用 SO 上的一些代码根据其内容设置表格首选宽度(我还覆盖了 JTablegetPreferredScrollableViewportSize 方法)。

这是我想要实现的截图:

JPanel 更宽,因为在我的应用程序中它将是CardLayout 的一部分,因此其他卡片的宽度和高度将决定此面板的大小(在下面的代码中,为了方便,我将覆盖 getPreferredSize 方法,但我不想使用它)。

这个结果可以用下面的代码来实现,你只需要改变这行代码:

Object [][] data = new Object [100][1];

到:

Object [][] data = new Object [10][1];

问题是当JTable 有更多行时,在这种情况下会出现垂直滚动条,但滚动窗格会“缩小”:

这是我正在使用的代码:

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
public class TableTest
{
    public static void main (String [] a) {
        SwingUtilities.invokeLater (new Runnable () {
            @Override public void run () {
                createAndShowGUI ();
            }
        });
    }
    private static void createAndShowGUI () {
        JFrame frame = new JFrame ("Table Test");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setContentPane (new MainPanel ());
        frame.pack ();
        frame.setLocationRelativeTo (null);
        frame.setVisible (true);
    }
}
class MainPanel extends JPanel
{
    private static int MAX_HEIGHT = 600;
    private static int MAX_WIDTH = 600;

    private JTable table;

    protected MainPanel () {
        super (new GridBagLayout ());
        Object [][] data = new Object [100][1];
        for (int i = 0; i < 10; i ++) data [i] = new String [] {"Some data ..."};
        table = new JTable (data, new String [] {""}) {
            @Override public Dimension getPreferredScrollableViewportSize () {
                int width = 0;
                for (int column = 0; column < getColumnCount (); column ++) width += columnModel.getColumn (column).getPreferredWidth ();
                return new Dimension (Math.min (MAX_WIDTH, width), Math.min (MAX_HEIGHT, getRowHeight () * getRowCount ()));
            }
        };
        table.setAutoResizeMode (JTable.AUTO_RESIZE_OFF);
        table.setShowGrid (false);
        table.setTableHeader (null);
        resizeColumns ();
        JScrollPane scrollPane = new JScrollPane (table);
        scrollPane.setBackground (table.getBackground ());
        scrollPane.getViewport ().setBackground (table.getBackground ());
        // --- Adding components ---
        GridBagConstraints c = new GridBagConstraints ();
        c.fill = GridBagConstraints.VERTICAL;
        c.weightx = 0.0;
        c.weighty = 1.0;
        add (scrollPane, c);
    }
    @Override public Dimension getPreferredSize () {
        return new Dimension (500, 400);
    }
    protected void resizeColumns () {
        for (int column = 0; column < table.getColumnCount (); column ++) {
            TableColumn tableColumn = table.getColumnModel ().getColumn (column);
            int width = 0;
            for (int row = 0; row < table.getRowCount (); row ++) {
                width = Math.max (table.prepareRenderer (table.getCellRenderer (row, column), row, column).getPreferredSize ().width, width);
            }
            tableColumn.setPreferredWidth (width + table.getIntercellSpacing ().width);
        }
    }
}

如果我将c.fill 设置为GridBagConstraints.BOTHc.weightx 为任何正值,JScrollPane 将水平和垂直填充父面板。

如何在不手动设置JScrollPane 大小的情况下使用表格的首选宽度?

编辑

我的代码非常少,因为我发现我的问题与表格列的数量无关。但我真正的程序必须处理更多的列和大量的行(在许多情况下有一百万或更多)。

JList 是我的第一次尝试,因为我喜欢使用原型值的方式,但加载数据太慢(也可以在运行时更新),所有 GUI 冻结超过200 000 行,也是因为我使用的是自定义渲染器。

另外,我已经尝试过使用BorderLayout,但我真的希望滚动窗格高度填满我的面板,我尝试了不同的解决方案,但仍然无法解决。

EDIT2

正如@camickr 建议的那样,我可以为我的面板使用 BorderLayout,在 BorderLayout.WEST 处添加滚动面板以垂直填充面板而不占用所有水平空间。这是目前最好的解决方案,但这会导致水平中心对齐丢失。有什么想法吗?

【问题讨论】:

  • 你试过用BoxLayout代替吗?
  • @crizzis 是的,我试过了,如果我使用 BoxLayout,结果与使用 GridBagConstraints.BOTH 相同:滚动窗格水平和垂直填充面板

标签: java swing jtable layout-manager gridbaglayout


【解决方案1】:

(在下面的代码中,为方便起见,我重写了 getPreferredSize 方法,但我不想使用它

我同意。摆脱覆盖。

降低框架高度时的绘制问题是因为 GridBagLayout 会在空间不足时以最小尺寸绘制组件。

所以,虽然总的来说我不喜欢使用任何套装???硬编码以下大小的方法似乎有效:

scrollPane.setMinimumSize(scrollPane.getPreferredSize());

这将防止滚动窗格宽度减小。

如果您不喜欢使用硬编码的尺寸,可以使用Relative Layout。这将很容易让您将组件居中。

那么基本代码是:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
rl.setFill( true );
setLayout(rl);

...

add(Box.createHorizontalGlue(), new Float(1));
add(scrollPane);
add(Box.createHorizontalGlue(), new Float(1));

【讨论】:

  • 感谢您的回复,在这个评论区回复有点困难,所以我编辑了我的问题。
  • ``另外,我已经尝试使用 BorderLayout,但我真的希望滚动窗格高度填满我的面板` - 这正是 BorderLayout 的工作方式 - 这就是我提出建议的原因.
  • 好吧,我很困惑,因为你说的是​​“... but the height will vary”。这是我目前最好的尝试,但这样我会失去中心对齐。在之前的编辑中,我说maybe i could try to put the scrollpane in BorderLayout.WEST, wrapping it into a panel with a FlowLayout to align it on the center ...,我删除了它,因为我知道 FlowLayout 不起作用,你有什么更好的办法将整个滚动窗格对齐在中心?
  • @Ansharja,仔细阅读问题。认为我有一个简单的解决方案。
  • 感谢您的所有努力,此解决方案有效。我将阅读RelativeLayout,但现在我更喜欢更简单的解决方案。我想知道是否最好覆盖getMinimumSize () 方法(public Dimension getMimimumSize () { return getPreferredSize ();})而不是使用setter 方法,因为数据和表格滚动窗格尺寸会在运行时发生变化。再次感谢您!
【解决方案2】:

您的布局问题是由您的GridBagConstraints 引起的(尤其是fillweightx) 这阻止了scrollPane 使用可用的水平空间。

代替

GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0.0;
c.weighty = 1.0;
add (scrollPane, c);

你应该使用

GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;  // !!
c.weightx = 1.0;                   // !!
c.weighty = 1.0;
add (scrollPane, c);

然后会是这样的:

【讨论】:

  • 感谢您的回复,但我已经在我的问题中说过:If i set c.fill to GridBagConstraints.BOTH and c.weightx to any positive value, the JScrollPane will fill the parent panel both horizontally and vertically. 这不是我想要实现的目标。
猜你喜欢
  • 2011-11-24
  • 2013-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
  • 2016-04-29
  • 2013-11-24
  • 2012-12-19
相关资源
最近更新 更多