Установить ширину столбца JTable в процентах

Мне нужно назначить фиксированную ширину нескольким столбцам JTable, а затем равную ширину всем остальным столбцам.

Предположим, что JTable имеет 5 столбцов. Первый столбец должен иметь ширину 100, а второй - ширину 150. Если оставшаяся ширина JTable равна 600 после установки ширины двух столбцов, я бы хотел равномерно разделить ее между тремя другими столбцами.

Проблема в том, что table.getParent().getSize().width часто равен 0, даже если он добавлен к JFrame и виден, поэтому я не могу использовать его в качестве основы.

Как мне это сделать?


person Tom Tucker    schedule 25.09.2013    source источник
comment
Если ответ работает для вас, примите его.   -  person Hungry Blue Dev    schedule 01.03.2014


Ответы (5)


Мне нужно назначить фиксированную ширину нескольким столбцам JTable, а затем равную ширину всем остальным столбцам.

Пусть режим изменения размера таблицы сделает всю работу за вас. Установите режим изменения размера для всех столбцов и установите минимальные/максимальные значения фиксированных столбцов:

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();
            }
        });
    }
}
person camickr    schedule 26.09.2013
comment
+1: column.setPreferredWidth() не работает, когда режим изменения размера равен AUTO_RESIZE_ALL_COLUMNS, поэтому необходимо использовать setMin/MaxWidth(). - person Matthieu; 24.06.2016

Я думаю, вам нужно использовать table.getPreferredSize() вместо этого. Попробуйте этот фрагмент кода:

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

    }
}

Как вы увидите, столбец Name будет иметь ширину 100, столбец Category будет иметь ширину 150, столбец Color будет соответствовать 70% остаточной ширины, а столбец Ranking будет соответствовать последним 30%.

Обновить

На основании этого комментария:

Спасибо, но это не сработает, если размер JFrame явно больше размера JTable...

Решением может быть игра с методами setMinWidth и setMaxWidth для исправления статической ширины столбцов, или вы можете реализовать свой собственный TableColumnModelListener. В приведенном выше примере замените setPreferredWith строк следующим образом и попробуйте установить желаемый размер фрейма:

    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);
        }
    });
person dic19    schedule 25.09.2013
comment
Спасибо, но это не сработает, если размер JFrame явно больше размера JTable... - person Tom Tucker; 25.09.2013
comment
Я вижу вашу точку зрения. Как насчет игры с методами setMinWidth и setMaxWidth? Например, для вашего первого столбца вы можете вызвать setMinWidth(100) и setMaxWidth(100). Его ширина всегда будет равна 100 независимо от размера кадра и даже при изменении размера кадра. - person dic19; 25.09.2013

Мне пришлось динамически устанавливать ширину столбцов таблицы для проекта, который должен был правильно отображаться при разных настройках DPI разрешения экрана. Идея моего решения пришла из ответа dic19.

В основном я получил предпочтительный размер JPanel, где был размещен JSrollPane JTable, и отлично работает. Пожалуйста, смотрите ниже

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

Надеюсь, это поможет кому-то

Спасибо

person bluchip.gr    schedule 12.11.2013

Он думает, что было бы проще использовать (и повторно использовать) относительное изменение размера компонента, если бы мы инкапсулировали его в собственный класс. Поскольку я тоже столкнулся с той же проблемой, я хотел бы опубликовать свой код здесь.

С точки зрения клиента я хотел бы сделать что-то вроде этого

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

Итак, сначала я определил интерфейс ComponentResize и реализовал 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);
    }
}

Интерфейс ComponentResize отделяет способ установки размера компонента от конкретных API. Например. ширину TableColumn можно установить с помощью setPreferredWidth(int), а размер JComponent можно установить с помощью setPreferredWidth(Dimension)

Затем я реализовал RelativeWidthResizer, который инкапсулирует логику вычисления относительной ширины.

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);
        }
    }
}
person René Link    schedule 29.11.2014

person    schedule
comment
Это лучший ответ, который я видел :) - person ; 17.08.2015
comment
Обратите внимание, что вы можете использовать TableColumnModel.getTotalColumnWidth() на случай, если ваша таблица включена в JScrollPane (что обычно и бывает). - person Matthieu; 24.06.2016
comment
немного опоздал на вечеринку, но columnWidthPercentage должен составлять 1,0, иначе ваша таблица будет расти очень быстро - person Lyle Rolleman; 29.12.2017