Set Column Width of JTable by Percentage

后端 未结 5 1883
傲寒
傲寒 2020-12-31 20:53

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 column

相关标签:
5条回答
  • 2020-12-31 21:29

    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();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-31 21:30

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-31 21:33

    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

    0 讨论(0)
  • 2020-12-31 21:43
    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-31 21:46

    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);
            }
        });
    
    0 讨论(0)
提交回复
热议问题