- Notes on
JTable - JTable has a lot of legacy baggage
- Probably simpler to use
SpringLayoutorGroupLayoutorMigLayoutto build a "Table"- Resizing might be tough
- Still a good idea to separate model afrom presentation
- Does not store its own data, uses
TableModel(see below) - Supports sorting & filtering via
RowSorter - Supports headers
- Supports column resizing
- Supports custom header rendering
- Supports custom cell rendering
- Supports editing values in cells
JTableaddress rows and columns only byint, zero based- Column rearranging does NOT affect the model
- Layout: https://docs.oracle.com/en/java/javase/20/docs/api/java.desktop/javax/swing/JTable.html#doLayout()
- Columns resize to respond to change in containing parent
- When you put
JTableinto aJScrollPane, the header stays on top- If you don’t wrap the
JTablein aJScrollPane, you must explicitly add the header
- If you don’t wrap the
- Managing rows is very different from managing columns
- GOTCHA:
JButtondoesn't work in aJTablerow - GOTCHA: Hard to control spacing
- GOTCHA: Layout is overly complex and not so useful
- GOTCHA: Cells cannot contain arbitrary components (eg. no
JButton)
JTable->TableColumnModel->TableColumn->TableCellRendererJTable->TableModel
- Resizing:
setRowHeight(...)and row specific version - Space between rows:
setRowMargin(margin)
- Resizing
- See
setAutoResizeMode(...)AUTO_RESIZE_SUBSEQUENT_COLUMNS: (default) Resize all subsequent columns equally, preserve total widthAUTO_RESIZE_OFF: Don’t resize columns; change the table width when parent size changes- ... other options aren't as useful
- See
- Space between columns
- TODO ...
- See
AUTO_RESIZE_OFF - GOTCHA: You cannot set just one column width, you must set all
getColumnModel().getColumn(i).setPreferredWidth(...) - GOTCHA:
does NOT workcol.setWidth
public static void setColumnWidths(
JTable table,
int totalTableWidth,
int... percentages) {
Objects.requireNonNull(table, "table is required and null.");
Objects.requireNonNull(percentages, "percentages is required and null.");
checkArgument(totalTableWidth > 0, "totalTableWidth must be positive > 0");
checkArgument(percentages.length == table.getColumnCount(),
"percentages must have the same length as the table columns");
Arrays.stream(percentages).forEach(p -> {
checkArgument(p >= 0, "percentages must be positive > 0");
checkArgument(p <= 100, "percentages must be <= 100");
});
final var pctSum = Arrays.stream(percentages).sum();
for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
var w = (percentages[i] / pctSum) * totalTableWidth;
table.getColumnModel()
.getColumn(i)
.setPreferredWidth(w);
}
}- Put the
JTableinto aJScrollPane - DON'T set preferred size on JTable, set
table.setFillsViewportHeight(true); - You can display the Header separately from the Body (see
getTableHeader()JScrollPanedoes this automatically
- Extend
AbstractTableModel, using ajava.util.Listof your own row type - Manages contents/data/values in each cell
- NOT for presentation, just data
- Use
Lockfor synchronization - Don't use
because it uses Vector and does some unnecessary lookupsDefaultTableModel - Your model should be mutable (to avoid reconnecting the model to Table & Sorter repeatedly)
- Allow replacement, adding, clearing of
Collection<YourRowType> fireTableDataChanged();after changing data- GOTCHA:
fireTableStructureChanged();replacesTableColumns andTableCellRenderers
- GOTCHA:
- Allow replacement, adding, clearing of
- Column rearranging does NOT affect the model
- Only a view/presentation trick
- No need to listen for column reorder events
RowSortersorting & filtering does NOT affect the model
- Manages column count, total width, selectability, etc
- Useful for dynamically adding/removing columns
TableColumnsbelong to the ColumnModel (not theJTable)- Column gaps defined here
- Maps between "coordinates" of rows in the model and rows in the UI
- to programmatically sort (eg. default sort on init), call
sort() - Apply sorter to table via
table.setRowSorter(...) - Row Selection uses the visible index, so convert using
convertRowIndexToView(...)orconvertRowIndexToModel(...)
- TODO: more here