Proper Override getPreferredScrollableViewportSize

I have a JTable inside a JScrollPane, and I override getPreferredScrollableViewportSize to fit the JScrollPane to a fixed number of table rows, allowing scroll bars to appear when needed. I also used parts of the code found in SO to resize the columns of the table to fit their content.

The problem I am facing is that when I dynamically add a wider row to the table, the horizontal JScrollBar is not displayed. I tried differently to get JTable, JViewport and JScrollPane to update correctly by calling revalidate () or repaint (), doLayout (), etc., but I didn’t.

Of course, I can recall the pack () method in the JFrame, but this will increase the width of the scroll bar, and the horizontal scroll bar will not appear anyway.

What am I missing?

There's MVCE here, it's just an example of an ugly toy, but that should be enough to reproduce unwanted behavior.

Thank you for your help!

The code:

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));
    }
}

EDIT:

Thanks to @camickr, I solved the problem by setting the desired auto resize mode and calling the resizeColumnWidth method every time a row is added. I already tried them separately, I can not believe that I did not use both :)

Hovewer, I am posting below updated code, now it works well:

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));
    }
}
+4
source share
1 answer

, JScrollBar .

:

setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

. , , , , .

resizeColumnWidth() ActionListener, .

Table Column Adjuster, , / TableModel.

+4

Source: https://habr.com/ru/post/1670253/


All Articles