正确重写 getPreferredScrollableViewportSize

问题描述 投票:0回答:1

我在 JScrollPane 中有一个 JTable,并且我重写了 getPreferredScrollableViewportSize 以使 JScrollPane 适合固定数量的表行,从而允许在需要时出现滚动条。

我还使用了 SO 上找到的部分代码来调整表列的大小以适应其内容。

我遇到的问题是,当我动态向表格添加更宽的行时,水平 JScrollBar 不显示。

我尝试了不同的方法来强制 JTable、JViewport 和 JScrollPane 正确更新,调用 revalidate(),或者 repaint()、doLayout() 等,但没有成功。

当然我可以回忆一下JFrame上的pack()方法,但是它会导致整个滚动窗格的宽度增长,并且水平滚动条仍然不会出现。

我错过了什么?

这里有一个 MVCE,它只是一个丑陋的玩具示例,但它应该足以复制不需要的行为。

感谢您的帮助!

代码:

import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.*;
public class Main
{
    public static void main (String [] a) {
        SwingUtilities.invokeLater (new Runnable () {
            public void run () {
                initGUI ();
            }
        });
    }
    private static void initGUI () {
        final DataTable table = new DataTable ();
        JPanel container = new JPanel (new BorderLayout (0, 20));
        container.add (new JScrollPane (table));
        container.add (new JButton (new AbstractAction ("Add wider row") {
            public void actionPerformed (ActionEvent e) {
                table.addRow ();
                // some of many useless attempts ...
                // table.resizeColumnWidth ();
                // table.revalidate ();
            }
        }), BorderLayout.SOUTH);
        container.setBorder (new EmptyBorder (10, 10, 10, 10));
        JFrame frame = new JFrame ("Test");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setResizable (false);
        frame.setContentPane (container);
        frame.setLocationRelativeTo (null);
        frame.pack ();
        frame.setVisible (true);
    }
}
class DataTable extends JTable
{
    private static final int VISIBLE_ROWS = 5;
    
    DataTable () {
        String [][] data = new String [][] {
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"}
        };
        setModel (new DefaultTableModel (data, new String [] {"", ""}) {
            @Override public boolean isCellEditable (int row, int column) {
               return false;
            }
        });
        columnModel.setColumnMargin (20);
        setBorder (null);
        setFocusable (false);
        setRowSelectionAllowed (false);
        setShowGrid (false);
        setTableHeader (null);
        resizeColumnWidth ();
    }
    public void addRow () {
        ((DefaultTableModel) getModel ()).addRow (new String [] {"SomeLongerText", "SomeLongerText"});
    }
    private int calculateColumnWidth (int column) {
        TableColumn tableColumn = columnModel.getColumn (column);
        int width = 0;
        for (int row=0; row<getRowCount(); row++) width = Math.max (prepareRenderer (getCellRenderer (row, column), row, column).getPreferredSize ().width, width);
        return width + getIntercellSpacing ().width;
    }
    @Override public Dimension getPreferredScrollableViewportSize () {
        int columnsWidth = 0;
        for (int column=0; column<getColumnCount (); column++) columnsWidth += calculateColumnWidth (column);
        return new Dimension (columnsWidth, VISIBLE_ROWS * getRowHeight ());
    }
    protected void resizeColumnWidth () {
        for (int column=0; column<getColumnCount (); column++) columnModel.getColumn (column).setPreferredWidth (calculateColumnWidth (column));
    }
}

编辑:

感谢@camickr,我解决了这个问题,设置所需的自动调整大小模式并在每次添加行时调用 resizeColumnWidth 方法。 我已经分别尝试过它们,我不敢相信我没有同时使用它们:)

然而,我在更新后的代码下面发布了,现在它运行良好:

import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.*;
public class Main
{
    public static void main (String [] a) {
        SwingUtilities.invokeLater (new Runnable () {
            public void run () {
                initGUI ();
            }
        });
    }
    private static void initGUI () {
        final DataTable table = new DataTable ();
        JPanel container = new JPanel (new BorderLayout (0, 20));
        container.add (new JScrollPane (table));
        container.add (new JButton (new AbstractAction ("Add wider row") {
            public void actionPerformed (ActionEvent e) {
                table.addRow ();
            }
        }), BorderLayout.SOUTH);
        container.setBorder (new EmptyBorder (10, 10, 10, 10));
        JFrame frame = new JFrame ("Test");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setResizable (false);
        frame.setContentPane (container);
        frame.setLocationRelativeTo (null);
        frame.pack ();
        frame.setVisible (true);
    }
}
class DataTable extends JTable
{
    private static final int VISIBLE_ROWS = 5;
    
    DataTable () {
        String [][] data = new String [][] {
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"},
            {"SomeText", "SomeText"}
        };
        setModel (new DefaultTableModel (data, new String [] {"", ""}) {
            @Override public boolean isCellEditable (int row, int column) {
               return false;
            }
        });
        columnModel.setColumnMargin (20);
        setAutoResizeMode (AUTO_RESIZE_OFF);
        setBorder (null);
        setFocusable (false);
        setRowSelectionAllowed (false);
        setShowGrid (false);
        setTableHeader (null);
        resizeColumnWidth ();
    }
    public void addRow () {
        ((DefaultTableModel) getModel ()).addRow (new String [] {"SomeLongerText", "SomeLongerText"});
        resizeColumnWidth ();
    }
    private int calculateColumnWidth (int column) {
        TableColumn tableColumn = columnModel.getColumn (column);
        int width = 0;
        for (int row=0; row<getRowCount(); row++) width = Math.max (prepareRenderer (getCellRenderer (row, column), row, column).getPreferredSize ().width, width);
        return width + getIntercellSpacing ().width;
    }
    @Override public Dimension getPreferredScrollableViewportSize () {
        int columnsWidth = 0;
        for (int column=0; column<getColumnCount (); column++) columnsWidth += calculateColumnWidth (column);
        return new Dimension (columnsWidth, VISIBLE_ROWS * getRowHeight ());
    }
    protected void resizeColumnWidth () {
        for (int column=0; column<getColumnCount (); column++) columnModel.getColumn (column).setPreferredWidth (calculateColumnWidth (column));
    }
}
java swing jtable jscrollpane preferredsize
1个回答
4
投票

当我向表格动态添加更宽的行时,水平 JScrollBar 不显示。

水平滚动条仅在您使用时才会出现:

setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

所有列都将以其首选大小显示。它们可能比视口小,在这种情况下您将看到空白空间,或者它们可能比视口更大,在这种情况下您将看到滚动条。

您需要在

resizeColumnWidth()
中调用
ActionListener
方法才能显示滚动条。

您还可以查看表格列调整器,它支持在 TableModel 中添加/更改数据时动态计算列大小。

© www.soinside.com 2019 - 2024. All rights reserved.