The JDisplay Java Applet
displays the large program listings on this web page.
JDisplay requires an up-to-date
browser and Java version
1.8+, preferably 1.8.0_131.
If you can’t see the listings, or if you just want to learn
more about JDisplay, click
Use Firefox for best results.
Swing’s component for displaying tables, much like a spreadsheet.
Failing to fire the appropriate change events, such as fireTableRowsInserted, in your add and
similar TableModel methods to let Swing know what
needs to be repainted.
When you modify the TableModel with a
Thread other than the Swing Thread, you must do most of your JTable
and TableModel calls with SwingUtilities, including firing changes via invokeLater from other Threads. You
further need locks in the TableModel to deal with
the problem of Swing accessing the model at the same time the app is changing
JTables are tricky. I strongly suggest buying a
textbook that covers them rather than trying to figure it out all on your own from
Classes to implement a JTable
You actually need a family of
JTable : the visible component.
TableModel : the part you write to manage the
data. When data change, the TableModel fires
events to let the GUI (Graphic User Interface) know to refresh
portions of the displayed table. You do this with AbstractTableModel. fireTableCellUpdated, fireTableRowsUpdated, fireTableRowsDeleted, fireTableDataChanged or fireTableStructureChanged. Use the most specific method, e.g.
fireTableCellUpdated in preference to fireTableRowsUpdated if you just updated a few cells. Don’t
worry about what is currently visible. Just fire your events for all changes to
the model. The GUI will figure out if any rendering is needed in
response. Data are organised in rows and columns. Cells can contain nulls which will paint happily as blank.
TableColumnModel : What order to display the
various TableColumns. It controls how wide each column
should be, with
// get information about all columns.final TableColumnModel columnModel = jTable.getColumnModel();// setting column widths:columnModel.getColumn(col ).setPreferredWidth(widthInPixels );columnModel.getColumn(col ).setMinWidth(widthInPixels );columnModel.getColumn(col ).setMaxWidth(widthInPixels );// adding a bit of extra space between the columns.columnModel.setColumnMargin(5 );
Note there is only one TableColumnModel object
that serves all the columns.
TableColumn : the information about one column, e.g.
what class of data it contains and what the header is. It also tracks which column
in the TableModel where the data come from.
TableColumn. setHeaderRenderer lets you configure a TableCellRenderer to produce a column header with special fonts and
colours. TableColumn. setCellRenderer lets you configure a TableCellRenderer for the data cells of the column. Here is how to
write a simple TableCellRenderer to render
TableCellRenderer : takes data and converts them
to some displayable Component to plug into the grid.
Use or extend DefaultTableCellRenderer or implement
TableCellRenderer from scratch. If you leave out
the setBackground on your Component, DefaultTableCellRenderer.
getTableCellRendererComponent will handle it for you,
automatically picking the normal or selected background. Rendering is crawling
with nasty gotchas.
JTableHeader : deals mainly with the ability to drag
columns around to new positions. Also knows the foreground and background colours
for the usual headers.
JScrollPane: usually encloses a JTable so that you can deal with a table bigger than will fit on
screen. You can control what is visible with getVerticalScrollBar(). setValue().
Watch out! Oracle’s table classes are inconsistently named. Sometimes they
begin with a J and sometimes not.
JTables are not as
flexible as HTML (Hypertext Markup Language) tables. Cells cannot span
multiple rows or multiple columns. It is possible to change the height of a row from
the default 16 pixels with JTable. setRowHeight or width of column with TableColumn. setWidth. In addition you can
arrange that the user can change the column widths with dragging.
JTabless are tricky. It is not that anything is that
difficult, it is just that there are so many classes and so many methods to master.
JTables are more like the plans for a do-it-yourself table
than a table-creating appliance. I strongly suggest buying a textbook that covers
them rather than trying to figure it out all on your own from the Javadoc.
How JTables Work
The key thing to understand is the data live only
in the DataModel. The only rendered data are what is
currently visible. There is no giant bit map scrolling off screen. There is no
corresponding giant grid of JComponents. There is not even
a single row of JComponents. A TableCellRenderer renders all its cells recycling the same
JComponent. Whenever the user scrolls, new rows are
fetched from the DataModel and rendered. The bit map
for the rendered rows still visible is slid down in a rapid bit/blt move. This scheme
allows rapid scrolling over a DataModel that might not
even be totally RAM-resident.
If your JTables are too
wide what can you do?
Put them in a JScrollPane so that the whole table
Let the user change the width of the columns to squeeze the data into the
available space, widening columns as needed.
Give the users shortcuts to configure various column width constellations.
It gets awkward if you try to use two or three rows per data item. You still
need a perfect grid. You can’t have cells that span more than one column as
you can in an HTML table or spreadsheet. Rendering is based on
identical data format/type in each cell of a column.
To delete a row, you can have an icon on each row to click, or you can select a
row and right click to select delete off a menu. If the cell contents are
immutable, you might allow the delete key on any cell.
Useful Classes and Interfaces
classes and interfaces useful in creating JTables
Classes and Interfaces Useful in Creating JTables
implements the TableModel with your own
class to represent each row.
Implements TableCellEditor for some common
Implements TableCellRenderer for some common
data types, including ImageIcon.
implements the TableModel with each row
represented by a Vector.
Represents the entire table.
Represents all the header columns.
Methods to track which rows are selected.
Methods to edit a single cell.
Methods to display a single cell, both data and headers.
Represents one column, widths etc. Used to attach TableCellRenderers for both data and headers.
information about all columns.
Represents the data in the table grid.
notify listeners that a table model has changed
Keep rows sorted.Java version 1.6 or later only.
JTables can be overwhelming. It is hard to plan
everything out before you start. So here is a muddle through approach. You add a
little bit, then compile. If you don’t understand everything just compose dummy
methods you can come back to later.
Define your JTable and a ScrollPane to contain it.
Use the JTable constructor that takes only one
parameter, the TableModel.
Define your custom TableModel class, perhaps
making it an inner class. Have it extend AbstractTableModel or DefaultTableModel.
Use AbstractTableModel if your rows are represented by a
proper class and DefaultTableModel if they are
represented by a Vector of fields. Don’t try to
compose your model totally from scratch by implementing every method in
Use your IDE (Integrated Development Environment) to provide skeletons for the methods you
absolutely must override.
Look over the methods in your base class to see which ones need overriding and
write the code.
Declare a RowOfCells class whose elements are the
data for one row. Pick a more descriptive name than that, please. Write getters and
setters for the fields.
Declare an ArrayListRowOfCellsallRows to hold all the data for
Define a set of constants to name each of the columns defining the 0-based
Write add, set,
remove etc. methods to let you change the rows in the
model. Base them on the corresponding ArrayList methods.
Make sure you call fireTableRowsInserted, fireTableRowsUpdated or fireTableRowsDeleted to notify Swing of the changes you made to the
Write your ObjectgetValueAt( introwIndex, intcolumnIndex ). Don’t change the Object to something else, or you won’t override the correct
method. It will need a switch to use a different
getter depending on the column.
Write your setValueAt( ObjectaValue, introwIndex, intcolumnIndex ). Don’t change the
Object to something else, or you won’t override
the correct method. You can leave this out or use a dummy if your table is not
Test your data-model modifying methods by using them to put some dummy data
into the table, rather than trying to handle keyboard input just yet. It may be
easier to add one column at a time to your model and get that column completely
working with custom renderers and editors. They you can clone that working,
debugged code for the other columns, rather than your first cut buggy code.
Hookup buttons or menu items to call the add and
remove row methods.
Set your column width with
Write a TableCellRenderer to chose the fonts and
colours of your heading. Install with TableColumn.
Add your persistence mechanism to load up allRows and
save on exit. Make sure suitable event changes get fired. You might save to disk,
to the registry via the Preferences mechanism or to a
Compose your custom TableCellRenderers for the data
in each column. Hook them up with TableColumn.
Add sorts to your TableModel, remembering to
fireTableDataChanged. You can trigger them with
TableHeader. addMouseListener. In Java version 1.6 or later
JTables have built-in javax.swing.table. TableRowSorters.
Add your editing and custom TableCellEditors. It can
be as simple as:
// attaching a TableCellEditor to a columntableColumn.setCellEditor(new DefaultCellEditor(comboBox ));
where comboBox is a JComboBox
scratch component the user is allowed to edit. DefaultCellEditor will handle popping up the scratch edit
component, initialising it and sending the new value to the TableModel. To do more elaborate edits, search for sample code
on the web. If you apply colours and fonts to your scratch components, it will
make it clearer to the user which cell he is editing. The crucial methods are:
getTableCellEditorComponent which converts an
Object value taken from from the TableModel via getValueAt to a
displayable GUI object, e.g. a JTextField.
getCellEditorValue which extracts a
String from the GUI
component and converts it to an Object suitable for
storing in the TableModel via setValueAt. You can just throw an Exception if you don’t like the value, perhaps make a
noise too. The value will revert to the previous if the user does not fix the
Add calls to stopEdit just prior to
any time you sort, insert, delete or replace rows. JTable does not do this by default. Without this code, a field being
edited will be improperly left where it was, ending up on the wrong line.
Insert code to make sure a given row is visible in your scroll region like
Add threads so that the computation is on its own thread with the Swing thread
free to keep the GUI up to date. Add synchronized to TableModel methods
as needed. Make sure the model is not locked when you call the various fire methods because those events will need to get at the model via
getValueAt to repaint the GUI.
Pepper your code with Thread.yield
() after you call methods that could change the GUI.
give the Swing thread a bash at processing the generated GUI-changing events.
Remember that all the JTable methods including
fireTableCellUpdated and brethren must be invoked only
from the Swing thread. This means the calls must often be wrapped in SwingUtilities. invokeLater. Normally
you need to make your TableModel thread-safe because
JTable will call its methods on the Swing thread to
discover the data values and your app will call its methods on some other thread to
set the data values.
Hook up Listeners to the ListSelectionModel if there is anything you want to when the
selection changes. ListSelectionEvent. getFirstIndex and ListSelectionEvent.
getLastIndex do not give you the bounds of selected
rows. They give you the range of rows whose selection status may have changed.
Review the methods of JTable, ListSelectionModel, DefaultTableModel and
DefaultTableCellRenderer reading the documentation and
looking for methods to tweak the implementation. When you are looking for a method,
scan all those classes. Often the method you are looking for is not where you
expect and is not called what you might expect. Using a text search tool on the
Javadoc can be a great help to find what you are looking for as can examining the
If you write a TableCellRenderer that extends
DefaultTableCellRenderer, the underlying getTableCellRendererComponent method keeps returning the same
recycled JLabel object. So if you add an icon to it for
example, you will effectively add that icon to all the columns, unless you
explicitly remove it for the columns that don’t need it.
You hook up a TableCellRenderer with either
to control rendering a specific column or
to control rendering of all objects of a given class. You use TableColumn. setHeaderRenderer to
control rendering of the header for a specific column. If you hook up two cell
renderers to the same column, they cascade (the output of the first becomes the
input to the second) and are both applied. The second does not
replace the first.
If you write a TableCellRenderer for headings,
make sure you call jTable. setAutoCreateColumnsFromModel( false
); otherwise your renderer will be ignored, even though you called
If in your TableCellRenderer you create your own
JLabels to return, rather than using the one from
DefaultTableCellRenderer, make sure you call
JLabel. setOpaque( true ) or else your JLabel.
setBackground will be ignored.
You will probably also want to call JLabel.setBorder(
newEmptyBorder(1, 1, 1, 1)
) to keep the division lines between columns and JLabel. setHorizontalAlignment(
SwingConstants. CENTER )
to ensure the labels are centred. Make sure you always set the alignment explicitly
if you ever set it or otherwise an alignment setting for one rendering will spill
over into another.
If you call JTable.showGrid(
false ), you must also call
( newDimension( 0, 0 ) );
to butt the cells right up against each other.
An alternative approach is to override the prepareRenderer method.
TableRowSorter is only available in
Java version 1.6 or later. Sun provides source for
TableSorter you can cannibalise to sort JTables in earlier JDKs (Java Development Kits).
TableRowSorter does not sort your model. It creates
an auxiliary sorted index into it.
TableRowSorter by default flips the sort from
ascending to descending and back when the user clicks a column header. The logic to
do this is handled elsewhere, not TableRowSorter. I have
not yet found the code. However, it does not display any indicators as to the
current column or whether it is sorted ascending or descending. I have not yet
figured out a clean way to implement the indicators that can’t possibly get
out of sync.
TZ: a Simple Example
Here is a simple example using a
JTable that uses a TableRowSorter to let the user sort by any of the columns. It displays
a list of all the time zones that