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?
public MyJFrame() {
initComponents();
resizeColumns();
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
resizeColumns();
}
});
}
//SUMS 100%
float[] columnWidthPercentage = {20.0f, 55.0f, 10.0f, 5.0f, 5.0f, 5.0f};
private void resizeColumns() {
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);
}
}
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();
}
});
}
}
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);
}
});
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
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);
}
}
}