【问题标题】:JTable column widths only changing after two requestsJTable 列宽仅在两次请求后更改
【发布时间】:2013-03-07 09:33:57
【问题描述】:

在我的应用程序中,我有一个两列表,它将在程序的整个生命周期中动态填充。我有一个名为updateTableWidths 的方法,调用它时会执行以下操作:

  • 使第一列与单元格内容所需的一样宽
  • 使第二列与单元格内容所需的一样宽
  • 如果整个表格比其容器宽,则启用水平滚动条
  • 否则,让最后一列拉伸以填充容器

它似乎工作得很好,但由于某些非常奇怪的原因,它需要被称为 两次(但仅在某些情况下,即当表格宽度增加而不是缩小时)。下面的 SSCCE 说明了我的困境。

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

@SuppressWarnings("serial")
public class TableResizeTest extends JComponent {

    private JFrame frame;
    private JScrollPane scrollPane;
    private DefaultTableModel tableModel;
    private JTable table;
    private JPanel panel;

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    TableResizeTest window = new TableResizeTest();
                    window.frame.setVisible(true);
                    window.frame.requestFocusInWindow();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public TableResizeTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame("Test");
        frame.setBounds(300, 300, 200, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        tableModel = new DefaultTableModel(new Object[]{"Col_1", "Col_2"},0);

        table = new JTable(tableModel);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
        table.setAlignmentY(Component.TOP_ALIGNMENT);
        table.setAlignmentX(Component.LEFT_ALIGNMENT);
        table.setBorder(new EmptyBorder(0, 0, 0, 0));
        table.setFillsViewportHeight(true);
        table.setTableHeader(null);
        table.setEnabled(false);
        table.getColumnModel().setColumnMargin(0);
        frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));

        panel = new JPanel();
        frame.getContentPane().add(panel);
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        JButton btnFillTableShort = new JButton("Fill table short");
        btnFillTableShort.setAlignmentX(Component.CENTER_ALIGNMENT);
        panel.add(btnFillTableShort);

        JButton btnFillTableLong = new JButton("Fill table long");
        btnFillTableLong.setAlignmentX(Component.CENTER_ALIGNMENT);
        panel.add(btnFillTableLong);

        JButton btnUpdateTableWidths = new JButton("Update table widths");
        btnUpdateTableWidths.setAlignmentX(Component.CENTER_ALIGNMENT);
        panel.add(btnUpdateTableWidths);
        btnUpdateTableWidths.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateTableWidths();
            }
        });
        btnFillTableLong.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fillTableLong();
            }
        });
        btnFillTableShort.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fillTableShort();
            }
        });
        scrollPane = new JScrollPane(table);
        scrollPane.setPreferredSize(new Dimension(200, 100));
        scrollPane.setMaximumSize(new Dimension(32767, 100));
        frame.getContentPane().add(scrollPane);
        scrollPane.setAlignmentY(Component.TOP_ALIGNMENT);
        scrollPane.setBorder(new LineBorder(new Color(0, 0, 0)));

        frame.pack();
    }

    public void fillTableShort() {
        tableModel.setRowCount(0);
        tableModel.addRow(new Object[]{"short", "short"});
    }

    public void fillTableLong() {
        tableModel.setRowCount(0);
        tableModel.addRow(new Object[]{"looooooooooooooooooooong", "looooooooooooooooooooong"});
    }

    public void updateTableWidths() {
        int col_1_width = 0;
        for (int row = 0; row < table.getRowCount(); row++) {
            TableCellRenderer renderer = table.getCellRenderer(row, 0);
            Component comp = table.prepareRenderer(renderer, row, 0);
            col_1_width = Math.max (comp.getPreferredSize().width, col_1_width);
        }
        col_1_width += table.getIntercellSpacing().width;
        col_1_width += 10;
        table.getColumn("Col_1").setMinWidth(col_1_width);
        table.getColumn("Col_1").setMaxWidth(col_1_width);
        System.out.println("col_1_width was set to " + col_1_width);

        int col_2_width = 0;
        for (int row = 0; row < table.getRowCount(); row++) {
            TableCellRenderer renderer = table.getCellRenderer(row, 1);
            Component comp = table.prepareRenderer(renderer, row, 1);
            col_2_width = Math.max (comp.getPreferredSize().width, col_2_width);
        }
        col_2_width += table.getIntercellSpacing().width;
        int tableWidth = col_2_width + col_1_width;
        if (scrollPane.getVerticalScrollBar().isVisible()) {
            tableWidth += scrollPane.getVerticalScrollBar().getWidth();
        }
        if (tableWidth > scrollPane.getWidth()) {
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            System.out.println("Auto resize was set to AUTO_RESIZE_OFF");
        } else {
            table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
            col_2_width = scrollPane.getWidth() + col_1_width;
            System.out.println("Auto resize was set to AUTO_RESIZE_LAST COLUMN");
        }
        table.getColumn("Col_2").setPreferredWidth(col_2_width);
        table.getColumn("Col_2").setMinWidth(col_2_width);
        System.out.println("col_2_width was set to " + col_2_width + "\n");
    }
}

您可以按照以下步骤重现该行为:

1) 启动应用

2) 点击“填表”按钮

3) 单击“更新表格宽度”按钮(在这种情况下只需单击一次)

4) 点击按钮“长填表”

5) 点击“更新表格宽度”按钮(第一次点击)

6) 点击“更新表格宽度”按钮(第二次点击)

第二次点击后正常显示。我有一些打印到控制台的调试信息,对于第一次和第二次单击,该方法似乎在做同样的事情:

col_1_width was set to 152
Auto resize was set to AUTO_RESIZE_OFF
col_2_width was set to 142

因此,由于在第一次运行期间发生了更改,因此第二次发生的情况并没有明显的差异。正如我之前提到的,这只发生在表列增加而不是缩小时。

我真的被难住了。有什么想法吗?

【问题讨论】:

    标签: java swing user-interface jtable preferredsize


    【解决方案1】:

    您可能还需要为第一列指定首选大小,才能正常使用滚动。尝试添加这一行:

    table.getColumn("Col_1").setPreferredWidth(col_1_width);
    

    updateTableWidths 方法中处理Col_1 计算的块中。它解决了已发布的 SSCCE 中的问题。

    编辑:

    setPreferredWidth 中存在对 maxWidth 和 minWidth 的依赖。以下是setPreferredWidth 的代码。所以顺序可能很重要。

    public void setPreferredWidth(int preferredWidth) {
        int old = this.preferredWidth;
        this.preferredWidth = Math.min(Math.max(preferredWidth, minWidth), maxWidth);
        firePropertyChange("preferredWidth", old, this.preferredWidth);
    }
    

    同样探索setMaxWidth表示它也可以设置首选宽度:

    public void setMaxWidth(int maxWidth) {
        int old = this.maxWidth;
        this.maxWidth = Math.max(minWidth, maxWidth);
        if (width > this.maxWidth) {
            setWidth(this.maxWidth);
        }
        if (preferredWidth > this.maxWidth) {
            setPreferredWidth(this.maxWidth);
        }
        firePropertyChange("maxWidth", old, this.maxWidth);
    }
    

    【讨论】:

    • 谢谢。是的,它确实解决了这个问题,但正如我在上面@MadProgrammer 的回答中提到的,我把它放在 最小/最大分配之后显然很重要。我还必须将第 2 列的 setPreferredSize 调用向下移动一行,使其低于该列的 setMinimumSize 调用。
    • 感谢您的详细说明,我认为可能是这样,但是您渴望比我(或根本)更快地深入研究源代码为您赢得了答案。 :-)
    【解决方案2】:

    尝试添加TableColumn#setWidth 和/或TableColumn#setPreferredWidth

    table.getColumn("Col_1").setWidth(col_1_width);
    table.getColumn("Col_1").setPreferredWidth(col_1_width);
    
    //...///
    
    table.getColumn("Col_2").setWidth(col_2_width);
    table.getColumn("Col_2").setPreferredWidth(col_2_width);
    

    【讨论】:

    • 有趣的是,在我发完帖子后,我尝试了(setPreferredWidth,而不是setWidth),但没有成功。但刚才我玩得更多,意识到我在minmax 之前设置了preferred 一行。将该行移动到 after min/max 就可以了。奇怪的是,这 3 行的 order 会有所不同!同样,我必须将第 2 列的 setPreferredWidth 调用移动到 setMinWidth 调用之后。
    • 另外,出于纯粹的学术好奇心,我仍然想知道,如果两次调用 minmax,为什么它在没有 preferred 调用的情况下工作。奇怪。
    • 这可能与模型如何更新和容器层次结构失效有关。就个人而言,我会尽可能使用 setWidth ,除非你绝对想要限制宽度的 column s(即它们根本无法调整大小)
    • 是的,我确实希望将第一列完全锁定。不过感谢您的信息和建议。
    猜你喜欢
    • 2019-10-14
    • 2021-09-16
    • 2013-08-16
    • 2013-06-19
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多