The111
The111

Reputation: 5867

JTable column widths only changing after two requests

In my application, I have a two column table which will be dynamically populated through the life of the program. I have a method called updateTableWidths which, when called, does the following:

It seems to work great, but for some really strange reason it needs to be called twice (but only in some cases, i.e. when table widths are growing rather than shrinking). The SSCCE below illustrates my dilemma.

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

Here is the sequence of steps you can follow to reproduce the behavior:

1) Launch the app

2) Click button "Fill table short"

enter image description here

3) Click button "Update table widths" (in this case only one click is needed)

enter image description here

4) Click button "Fill table long"

enter image description here

5) Click button "Update table widths" (first click)

enter image description here

6) Click button "Update table widths" (second click)

enter image description here

After the second click, it displays properly. I have some debug info that prints to the console, and for both the first and second click, the method seems to be doing exactly the same thing:

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

So it's not like there is some obvious difference happening the second time around as a result of something that was changed during the first run. And as I mentioned earlier, this only happens when table columns have grown, rather than shrunk.

I am really stumped here. Any ideas?

Upvotes: 2

Views: 876

Answers (2)

tenorsax
tenorsax

Reputation: 21243

You probably need to specify the preferred size for the first column as well to work properly with the scroll. Try adding this line:

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

in the the block that handles Col_1 calculations in updateTableWidths method. It solved the issue in the posted SSCCE.

EDIT:

There is a dependency on maxWidth and minWidth in setPreferredWidth. Below is the code for setPreferredWidth. So the order may matter.

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

Also exploring setMaxWidth indicates that it may set preferred width as well:

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

Upvotes: 4

MadProgrammer
MadProgrammer

Reputation: 347332

Try adding TableColumn#setWidth and/or TableColumn#setPreferredWidth

ie

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

Upvotes: 3

Related Questions