Home > Software engineering >  Resize JTable to fit contents does not work precisely, resulting in elipses
Resize JTable to fit contents does not work precisely, resulting in elipses

Time:01-19

I want to perfectly resize a JTable to fit its model contents based on either the column header label, OR the row containing the longest String for the same column (whichever is greatest). I don't want to use the renderer to determine these sizes as I want to dynamically economize as much screen real-estate as possible based on the text values.

Now if the user resizes the frame LARGER than the contents of the table, then the extra space is given to the last column.

This is a very specific table that should not be reordered. No header column resizing allowed either.

The code I have does all this (almost). For some reason, the table shows ... (elipses) near the end of every single column even though I know FontMetrics suggests I have the correct text length to perfectly fit.

Is this a bug with Java Swing? How to get around this in an elegant manner?

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

public class Scrollable_Table_Example extends JFrame {

    private static final long serialVersionUID = 1L;

    private JPanel _panel;

    private JTextArea _text;
    private JTable _table;
    private DefaultTableModel _model;
    private JScrollPane _scroll;

    public static void main(String[] args) {
        Scrollable_Table_Example app = new Scrollable_Table_Example();
        app.setVisible(true);
    }   

    public Scrollable_Table_Example() {
        _panel = new JPanel(new BorderLayout());
        setPreferredSize(new Dimension(300, 500));
 
        _text = new JTextArea(300, 300);
        _text.setEditable(false);

        JScrollPane areaScrollPane = new JScrollPane(_text);
        areaScrollPane.setPreferredSize(new Dimension(250, 250));

        areaScrollPane.setHorizontalScrollBarPolicy(
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        areaScrollPane.setVerticalScrollBarPolicy(
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

        areaScrollPane.add(_text);
        _panel.add(areaScrollPane, BorderLayout.NORTH);

        _model = new DefaultTableModel();
        _model.addColumn("Type");
        _model.addColumn("Schema");
        _model.addColumn("Module Name");
        _model.addColumn("Path");

        _table = new JTable();
        _table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        _table.getTableHeader().setResizingAllowed(false);
        _table.getTableHeader().setReorderingAllowed(false);
        _table.setModel(_model);

        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                resizeColumnWidth(_table);
            }
        });
 
        for(int i = 0; i < 20; i  ) {
            Vector<String> r  = new Vector<String>();
            r.addElement("TABLE");
            r.addElement("dbo");
            r.addElement("Policy");
            r.addElement("OBJECTS -> INSURANCE -> Policy");
            _model.addRow(r);
        }

        _scroll = new JScrollPane(_table);
        _panel.add(_scroll, BorderLayout.CENTER);
        add(_panel);
        pack();
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setLocationRelativeTo(null);
    }   

    public static void resizeColumnWidth(JTable table) {
        final TableColumnModel columnModel = table.getColumnModel();
        FontMetrics fontMetrics = table.getFontMetrics(table.getFont());

        for (int column = 0; column < table.getColumnCount(); column  ) {
            String headerValue = columnModel.getColumn(column).getHeaderValue().toString();
            int headerWidth = fontMetrics.stringWidth(headerValue);
            int width = headerWidth;

            for (int row = 0; row < table.getRowCount(); row  ) {
                String columnValue = table.getModel().getValueAt(row, column).toString();
                int columnValueWidth = fontMetrics.stringWidth(columnValue);
                width = Math.max(width, Math.max(headerWidth, columnValueWidth));
            }
            columnModel.getColumn(column).setPreferredWidth(width);
        }

        int parentWidth = table.getParent().getSize().width;
        int tableWidth = table.getPreferredSize().width;
        
        if(parentWidth > tableWidth) {
            int diff = parentWidth - tableWidth;
            int newWidth = columnModel.getColumn(3).getPreferredWidth()   diff;
            columnModel.getColumn(3).setPreferredWidth(newWidth);
        }
    }
}

CodePudding user response:

The best I can do is to take margin from TableModel and multiply by 3. Thank you to MadProgrammer and Gilbert for your guidance. This seems to be pixel perfect ...

public static final void resizeColumnWidth(JTable table) {
    final TableColumnModel columnModel = table.getColumnModel();
    int margin = columnModel.getColumnMargin() * 3;
    
    
    System.out.println(margin);
    
    FontMetrics fontMetrics = table.getFontMetrics(table.getFont());

    for (int column = 0; column < table.getColumnCount(); column  ) {
        String headerValue = columnModel.getColumn(column).getHeaderValue().toString();
        int headerWidth = fontMetrics.stringWidth(headerValue)   margin;
        int width = headerWidth;

        for (int row = 0; row < table.getRowCount(); row  ) {
            String columnValue = table.getModel().getValueAt(row, column).toString();
            int columnValueWidth = fontMetrics.stringWidth(columnValue)   margin;
            width = Math.max(width, Math.max(headerWidth, columnValueWidth));
        }
        columnModel.getColumn(column).setPreferredWidth(width);
    }

    int parentWidth = table.getParent().getSize().width;
    int tableWidth = table.getPreferredSize().width;
    
    if(parentWidth > tableWidth) {
        int diff = parentWidth - tableWidth;
        int newWidth = columnModel.getColumn(3).getPreferredWidth()   diff;
        columnModel.getColumn(3).setPreferredWidth(newWidth);
    }           
}
  • Related