Set Column Width of JTable by Percentage
Asked Answered
E

5

8

I need to assign a fixed width to a few columns of a JTable and then an equal width to all the other columns.

Suppose a JTable has 5 columns. The first column should have a width of 100 and the second one a width of 150. If the remaining width of the JTable is 600 after setting the width of the two columns, I'd like to evenly split it among the other three columns.

The problem is table.getParent().getSize().width is often 0, even if it is added to the JFrame and visible, so I can't use it as a basis.

How do I go about doing this?

Espousal answered 25/9, 2013 at 18:40 Comment(1)
If an answer works for u, accept it.Berstine
C
16
public MyJFrame() {
    initComponents();
    resizeColumns();
    addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            resizeColumns();
        }
    });
}
//SUMS 1
float[] columnWidthPercentage = {0.2f, 0.55f, 0.1f, 0.05f, 0.05f, 0.05f};

private void resizeColumns() {
    // Use TableColumnModel.getTotalColumnWidth() if your table is included in a JScrollPane
    int tW = jTable1.getWidth();
    TableColumn column;
    TableColumnModel jTableColumnModel = jTable1.getColumnModel();
    int cantCols = jTableColumnModel.getColumnCount();
    for (int i = 0; i < cantCols; i++) {
        column = jTableColumnModel.getColumn(i);
        int pWidth = Math.round(columnWidthPercentage[i] * tW);
        column.setPreferredWidth(pWidth);
    }
}
Civility answered 25/9, 2014 at 19:41 Comment(3)
This is the best answer I have seen :)Mojgan
Note that you can use TableColumnModel.getTotalColumnWidth() in case your table is included in a JScrollPane (which is usually the case).Cleland
little late to the party but columnWidthPercentage should add up to 1.0, otherwise your table will grow very fastDiluvial
E
5

I need to assign a fixed width to a few columns of a JTable and then an equal width to all the other columns.

Let the table's resize mode do the work for you. Set the resize mode to all columns and set the min/max values of the fixed columns:

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableLayout extends JPanel
{
    public TableLayout()
    {
        setLayout( new BorderLayout() );

        JTable table = new JTable(5, 7);
        add( new JScrollPane( table ) );

        table.setAutoResizeMode( JTable.AUTO_RESIZE_ALL_COLUMNS );
        TableColumn columnA = table.getColumn("A");
        columnA.setMinWidth(100);
        columnA.setMaxWidth(100);
        TableColumn columnC = table.getColumn("C");
        columnC.setMinWidth(50);
        columnC.setMaxWidth(50);
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("TableLayout");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new TableLayout() );
        frame.setSize(600, 200);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}
Eckenrode answered 26/9, 2013 at 1:48 Comment(1)
+1: the column.setPreferredWidth() doesn't work when the resize mode is AUTO_RESIZE_ALL_COLUMNS so setMin/MaxWidth() have to be used.Cleland
L
3

I think you need to use table.getPreferredSize() instead. Try this code snippet:

import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;


public class Tests {

    private void initGUI(){        
        Object[] tableHeader = new Object[]{"Name", "Category", "Color","Ranking"};
        DefaultTableModel dftm = new DefaultTableModel(tableHeader, 0);        
        dftm.addRow(new Object[]{"Watermelon","Fruit","Green and red",3});
        dftm.addRow(new Object[]{"Tomato","Vegetable","Red",5});
        dftm.addRow(new Object[]{"Carrot","Vegetable","Orange",2});

        JTable table = new JTable(dftm);

        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setViewportView(table);

        Dimension tableSize = table.getPreferredSize();
        table.getColumn("Name").setPreferredWidth(100);
        table.getColumn("Category").setPreferredWidth(150);
        table.getColumn("Color").setPreferredWidth(Math.round((tableSize.width - 250)* 0.70f));
        table.getColumn("Ranking").setPreferredWidth(Math.round((tableSize.width - 250)* 0.30f));

        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(scrollPane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                 new Tests().initGUI();
            }
        });        

    }
}

As you'll see, Name column will have a width of 100, Category will have a width of 150, Color column will fit at 70% of remanent width and Ranking will fit at last 30%.

Update

Based on this comment:

Thanks, but this will not work if the JFrame's size is set explicitly larger than the JTable...

Solution could be play with setMinWidth and setMaxWidth methods to fix static columns width, or you can implement your own TableColumnModelListener. In the example above replace setPreferredWith lines as follows and try set frame's preferred size as you wish:

    final JTable table = new JTable(dftm);        
    table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {

        @Override
        public void columnAdded(TableColumnModelEvent e) {
            table.columnAdded(e);
        }

        @Override
        public void columnRemoved(TableColumnModelEvent e) {
            table.columnRemoved(e);
        }

        @Override
        public void columnMoved(TableColumnModelEvent e) {
            table.columnMoved(e);
        }

        @Override
        public void columnMarginChanged(ChangeEvent e) {
            Dimension tableSize = table.getSize();
            table.getColumn("Name").setWidth(100);
            table.getColumn("Category").setWidth(150);
            table.getColumn("Color").setWidth(Math.round((tableSize.width - 250)* 0.70f));
            table.getColumn("Ranking").setWidth(Math.round((tableSize.width - 250)* 0.30f));
        }

        @Override
        public void columnSelectionChanged(ListSelectionEvent e) {
            table.columnSelectionChanged(e);
        }
    });
Lacedaemonian answered 25/9, 2013 at 19:14 Comment(2)
Thanks, but this will not work if the JFrame's size is set explicitly larger than the JTable...Espousal
I see your point. What about play with setMinWidth and setMaxWidth methods? For instance for your first column you can call setMinWidth(100) and setMaxWidth(100). Its width will always be 100 regardless frame's size and even on frame's resizing.Lacedaemonian
M
0

I had to dynamically set my table columns width for a project that had to display correctly on different screen resolution DPI settings. The idea for my solution came from dic19s answer.

Basically I got the preferred size of the JPanel where the JSrollPane of the JTable was placed and work great. Please see below

Dimension tableSize =  tableScrollPanePanel.getPreferredSize();
table.getColumnModel().getColumn(0).setPreferredWidth(Math.round(tableSize.width*0.35f));
table.getColumnModel().getColumn(1).setPreferredWidth(Math.round(tableSize.width*0.215f));
table.getColumnModel().getColumn(2).setPreferredWidth(Math.round(tableSize.width*0.20f));
table.getColumnModel().getColumn(3).setPreferredWidth(Math.round(tableSize.width*0.10f));
table.getColumnModel().getColumn(4).setPreferredWidth(Math.round(tableSize.width*0.10f));

Hope this helps somebody

Thanks

Mullock answered 12/11, 2013 at 19:30 Comment(0)
C
0

It think it would be easier to use (and re-use) relative component resizing if we encapsulate it in an own class. Since I also was confronted with the same issue, I would like to post my code here.

From a client perspective I would like to do something like this

TableColumnModel columnModel = jTable.getColumnModel();

TableColumn firstColumn = columnModel.getColumn(0);
TableColumn secondColumn = columnModel.getColumn(1);

ComponentResize<TableColumn> columnResize = new TableColumnResize();
RelativeWidthResizer<TableColumn> relativeWidthResizer = new RelativeWidthResizer<TableColumn>(columnResize);

relativeWidthResizer.setRelativeWidth(firstColumn, 0.8);
relativeWidthResizer.setRelativeWidth(secondColumn, 0.2);

jTable.addComponentListener(relativeWidthResizer);

So I first defined the ComponentResize interface and implement a TableColumnResize

public interface ComponentResize<T> {
    public void setWidth(T component, int width);
}

public class TableColumnResize implements ComponentResize<TableColumn> {

    public void setWidth(TableColumn component, int width) {
        component.setPreferredWidth(width);
    }
}

The ComponentResize interface decouples the way a component's size is set from the concrete APIs. E.g. a TableColumn's width can be set via setPreferredWidth(int) while a JComponent's size can be set by setPreferredWidth(Dimension)

Than I implemented the RelativeWidthResizer that encapsulates the relative width calculation logic.

public class RelativeWidthResizer<T> extends ComponentAdapter {

    private Map<T, Double> relativeWidths = new HashMap<T, Double>();
    private ComponentResize<T> componentResize;

    public RelativeWidthResizer(ComponentResize<T> componentResize) {
        this.componentResize = componentResize;
    }

    public void setRelativeWidth(T component, double relativeWidth) {
        if (relativeWidth < 0.0) {
            throw new IllegalArgumentException(
                    "Relative width must be greater or equal to 0.0");
        }

        if (relativeWidth > 1.0) {
            throw new IllegalArgumentException(
                    "Relative width must be less or equal to 1.0");
        }

        double totalRelativeWidth = 0.0;
        for (Double relativeComponentWidth : relativeWidths.values()) {
            totalRelativeWidth += relativeComponentWidth.doubleValue();
        }

        double availableRelativeWidth = 1.0d - (totalRelativeWidth + relativeWidth);

        boolean totalPercentageExceeded = availableRelativeWidth < 0;
        if (totalPercentageExceeded) {
            double remainingRelativeWidth = 1.0d - totalRelativeWidth;
            String message = MessageFormat.format(
                    "Can't set component's relative width to {0}."
                            + " {1} relative width remaining", relativeWidth,
                    remainingRelativeWidth);
            throw new IllegalArgumentException(message);
        }

        relativeWidths.put(component, relativeWidth);
    }

    @Override
    public void componentResized(ComponentEvent e) {
        Component component = e.getComponent();
        apply(component);
    }

    public void apply(Component baseComponent) {
        Dimension size = baseComponent.getSize();
        int maxWidth = (int) size.getWidth();

        int remaining = maxWidth;

        Set<Entry<T, Double>> entrySet = relativeWidths.entrySet();
        Iterator<Entry<T, Double>> entrySetIter = entrySet.iterator();

        while (entrySetIter.hasNext()) {
            Entry<T, Double> componentEntry = entrySetIter.next();
            T componentToResize = componentEntry.getKey();
            Double relativeWidth = componentEntry.getValue();

            int width = (int) (maxWidth * relativeWidth.doubleValue());
            remaining -= width;

            boolean lastComponent = !entrySetIter.hasNext();
            if (lastComponent && remaining > 0) {
                width += remaining;
            }
            componentResize.setWidth(componentToResize, width);
        }
    }
}
Cyrenaica answered 29/11, 2014 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.