|
Bookshelf Index
Chapter 18, Tables
By Matthew Robinson and Pavel Vorobiev
Introduction |
Download Chapter 18| Download Chapter 22 << NEXT >>
18.1.9 DefaultTableCellRenderer
class javax.swing.table.DefaultTableCellRenderer
This is the concrete default
implementation of the TableCellRenderer
interface. DefaultTableCellRenderer
extends JLabel and is
used as the default class-based renderer for Number,
Icon, and Object
data types. Two private Color
variables are used to hold selected foreground and background
colors
which are
used to render the cell if it is editable and if it has the
current
focus.
These colors can be assigned with
DefaultTableCellRenderer's
overridden setBackground() and
setForeground()
methods.
A protected Border property is used to store the
border
that is used when the cell does not have the current focus. By
default
this is
an EmptyBorder with
top and bottom space of 1, and left and right space of 2.
Unfortunately
DefaultTableCellRenderer
does not provide a method to change this border.
DefaultTableCellRenderer renders the value object
passed as parameter to its getTableCellRenderer()
method by setting its label text to the String
returned
by that
object's toString() method. Note
that all default JLabel
attibutes are used in rendering. Also note that we can do
anything to
this
renderer that we can do to a JLabel,
such as assign a tooltip or a disabled/enabled state.
Note: JTable can have a tooltip assigned to
it
just as any other Swing component. However, tooltips assigned to
renderers
take
precedence over that assigned to JTable,
and in the case that both are used the renderer's tooltip text
will be
displayed when the mouse lies over a cell using it.
18.1.10 The TableCellEditor Interface
abstract interface
javax.swing.table.TableCellEditor
This interface extends CellEditor and describes
the editor used to edit cell data in a TableColumn.
Each
TableColumn has an associated
TableCellEditor which
can
be assigned/retrieved with the
setCellEditor()/getCellEditor()
methods.
The getTableCellEditorComponent()
method is the only method declared by this interface, and is
expected
to
return
a Component that will
be used to allow editing of a cell's data value. It takes the
following
parameters:
JTable table:
the table instance containing the cell to be rendered.
Object value:
the value used to represent the data in the given cell.
boolean
isSelected: whether or not the given cell is selected.
int row:
can be used to return a renderer component specific to row or
cell.
int column:
can be used to return a renderer component specific to column or
cell.
We are expected to customize or vary
the returned component based on the given parameters. For
instance,
given a
value that is an instance
of Color, we might
return a special JComboBox
which lists several color choices. This method can be used to
return
different
editor components on a column, row, or cell-specific basis, and
is
similar to
JTree's TreeCellEditor
getTreeCellEditorComponent()
method.
Note: The row and column
parameters refer to the location of data in the
TableModel,
not a cell location in the TableColumnModel.
As with table cell renderers, each TableColumn has a
column-based editor associated with it. By default this editor is
null and can be
assigned/retrieved with TableColumn's
setCellEditor()/getCellEditor()
methods.
Unlike renderers, table cell editors are completely interactive
and do
not
simply act as rubber stamps.
TableCellEditor implementations must also implement
methods defined in the CellEditor
interface: addCellEditorListener(),
removeCellEditorListener(),
cancelCellEditing(),
stopCellEditing(), isCellEditable(),
shouldSelectCell(), and
getCellEditorValue(). The
isCellEditable() method is
expected to be used in combination with TableModel's
isCellEditable() method to determine whether a
given cell can be edited. Only in the case that both return
true
is
editing allowed.
(See discussion of the CellEditor
interface in section 17.1.13 for more about each of these
methods.)
To initiate cell editing on a given
cell, JTable listens
for mouse presses and invokes its editCellAt()
method in
response.
This method queries both the TableModel and the
appropriate cell editor to determine if the given cell can be
edited.
If so
the
editor component is retrieved with
getTableCellEditorComponent()
and placed in the given cell (its
bounds are adjusted so that it will fit within the current cell
bounds). Then
JTable adds itself to the
editor component (recall that JTable
implements the CellEditorListener
interface) and the same mouse event that sparked the edit gets
sent to
the
editor component. Finally the cell editor's
shouldSelectCell()
method is invoked to determine
whether the row containing that cell should become selected.
The default implementation of TableCellEditor is
provided
as DefaultCellEditor.
Unfortunately DefaultCellEditor
is not easily extensible and we are often forced to implement all
TableCellEditor and CellEditor
functionality
ourselves.
18.1.11 DefaultCellEditor
class javax.swing.DefaultCellEditor
DefaultCellEditor is a concrete
implementation of the TableCellEditor
interface and also the TreeCellEditor
interface. This editor is designed to return either a
JTextField,
JComboBox, or JCheckBox for cell
editing. It is used by both JTable and JTree components and
is discussed 17.1.15.
18.1.12 The TableModelListener Interface
abstract interface javax.swing.event.TableModelListener
This interface describes an object
that listens to changes in a TableModel.
Method tableChanged()
will be invoked to notify us of these changes.
TableModel's
addTableModelListener() and
removeTableModelListener()
methods are used
to add and remove TableModelListeners
respectively (they are not added directly to
JTable).
18.1.13 TableModelEvent
class javax.swing.TableModelEvent
This event extends EventObject and is used to
notify TableModelListeners
registered with TableModel
about changes in that model. This class consists of four
properties
each
accessible with typical get methods:
int column:
specifies the column affected by the change.
TableModelEvent.ALL_COLUMNS is used to indicate
that more than one column is affected.
int firstRow:
specifies the first row affected.
TableModelEvent.HEADER_ROW can
be
used here to indicate that
the name, type, or order of one or more columns has changed.
int lastRow:
the last row affected. This value should always be greater than
or
equal to
firstRow.
int type:
the type of change that occured. Can be any of
TableModelEvent.INSERT,
TableModelEvent.DELETE, or
TableModelEvent.UPDATE. INSERT and
DELETE
indicate the
insertion and deletion of rows respectively. UPDATE
indicates
that
values have changed but the
number of rows and columns has not changed.
As with any EventObject we can retrieve the
source
of a
TableModelEvent with getSource().
18.1.14 The TableColumnModelListener interface
abstract interface
javax.swing.event.TableColumnModelListener
This interface describes an object
that listens to changes in a TableColumnModel:
the adding, removing and movement of columns, as well as changes
in
margin
size
and the current selection. TableColumnModel
provides methods for adding and removing these listeners:
addTableColumnModelListener()
and removeTableColumnModelListener().
(As is the case with TableModelListeners,
TableColumnModelListeners
are not directly added to JTable.)
Five methods are declared in this
interface and must be defined by all implementations:
columnAdded(TableColumnModelEvent),
columnRemoved(TableColumnModelEvent),
columnMoved(TableColumnModelEvent),
columnMarginChanged(TableColumnModelEvent),
and columnSelectionChanged(ListSelectionEvent).
ListSelectionEvents are
forwarded to TableColumnModel's
ListSelectionModel.
18.1.15TableColumnModelEvent
class javax.swing.event.TableColumnModelEvent
This event extends EventObject and is used to
notify TableColumnModel
about changes to a range of columns. These events are passed to
TableColumnModelListeners.
The fromIndex
property specifies the lowest index of the colmn in the
TableColumnModel affect by the change. The
toIndex
specifies the
highest index. Both can be retrieved with typical get accessors.
A
TableColumnModel fires
a TableColumnModelEvent whenever a column move,
removal, or addition occurs. The event source can be retrieved
with
getSource().
18.1.16 JTableHeader
class javax.swing.table.JTableHeader
This GUI component (which looks like
a set of buttons for each column) is used to display a table's
column
headers.
By dragging these headers the user can rearrange a table's
columns
dynamically.
This component is used internally by JTable. It can
be
retrieved
with JTable's getTableHeader() method,
and
assigned
with setTableHeader(). When a JTable is
placed in a
JScrollPane a default JTableHeader
corresponding
to each column is added to that scroll pane's
COLUMN_HEADER
viewport (see 7.1.3). Each JTable uses one
JTableHeader instance.
JTableHeader extends JComponent and
implements
TableColumnModelListener. Though
JTableHeader is a
Swing
component, it is not used for display purposes. Instead, each
TableColumn maintains a
specific TableCellRenderer
implementation used to represent its header. By default this is
an
instance of
DefaultTableCellRenderer
(see 18.1.8).
Note: It is
more common to customize the header renderer of a
TableColumn
than
it is to customize a table's JTableHeader. In most
cases
the
default
headers provided by JTable are
satisfactory.
The resizingAllowed property specifies whether or
not
columns can be resized (if this is false it
overpowers
the
isResizable property of each
TableColumn).
The
reorderingAllowed property
specifies whether or not columns can be reordered, and
updateTableInRealTime property specifies
whether or not the whole column is displayed along with the
header as
it is
dragged (this in only applicable if
reorderingAllowed is
true). All three of these properties are
true by
default.
Column resizing: It is best to try and isolate
columns which need to be fixed width, for example the display of
monetary
amounts which might be 10 significant figures with two decimal
places.
Such a
column requires a fixed width. It doesn't need to be bigger and
it
doesn't
want
to be smaller. Allow the other columns to vary in size around the
fixed
columns.
For example in a two column
table displaying Product Description and Price, fix the size of
Price
and
allow
Description to resize.
Draggable columns, added flexibility, added complexity:
If you don't need the flexibility of draggable table columns then
it is best to switch them off. If a User accidentally picks up a
JHeader
component and
re-arranges a table, this could be confusing and upsetting. They
may
not
realise what they have done or how to restore the table to its
original
form.
At any given time during a column
drag we can retrieve the distance, in table coordinates, that the
column has
been dragged with respect to its original position from the
draggedDistance property. JTableHeader
also
maintains
a reference to the TableColumn
it represents as well as the JTable
it is part of -- the tableColumn
and table properties
respectively.
18.1.17 JTable selection
JTable supports two selection models: one for row
selections and one for column selections. JTable
also
supports
selection of individual table
cells. Column selections are managed by a
ListSelectionModel
maintained by a TableColumnModel
implementation, and row selections are managed by a
ListSelectionModel
maintained by JTable itself (both are
DefaultListSelectionModels
by default). As we learned in chapter 10,
ListSelectionModels
support three selection modes: SINGLE_SELECTION,
SINGLE_INTERVAL_SELECTION,
and MULTIPLE_INTERVAL_SELECTION.
JTable provides the setSelectionMode()
methods
which will set both selection models to the given mode. Note,
however,
that
getSelectionMode() only
returns the current row selection mode.
To assign a specific selection mode
to JTable's column
ListSelectionModel:
myJTable.getSelectionModel(
).setSelectedMode(
ListSelectionModel.XX_SELECTION);
To assign a specific selection mode to JTable's
column
ListSelectionModel:
myJTable.getColumnModel(
).getSelectionModel(
).setSelectionMode(
ListSelectionModel.XX_SELECTION);
|
Row selection mode defaults to
MULTIPLE_INTERVAL_SELECTION,
and column selection mode defaults to
SINGLE_SELECTION_MODE.
JTable provides control over whether rows and and
columns can be selected, and we can query these modes, and turn
them
on/off,
with getRowSelectionAllow(),
getColumnSelectionAllowed(),
and setRowSelectionAllowed(),
setColumnSelectionAllowed()
respectively. When row selection is enabled (true by
default) and
cell selction is disabled (see
below), clicking on a cell will select the entire row that cell
belongs to.
Similarly, when column selection is enabled (false
by
default)
the
whole column that cell
belongs to will be selected. There is nothing stopping us from
having
both row
and column selection active simultaneously.
JTable also provides control over whether
individual cells can be selected with its
cellSelectionEnabled
property. We can turn this on
or off with setCellSelectionEnabled()
and query its state using getCellSelectionEnabled().
If cell selection is enabled (false
by default), a cell can only be selected if both row selection
and
column
selection are also enabled (see below). If cell selection is not
enabled,
whenever a row or column containing that cell is selected
(assuming
that
either
row and/or column selection is enabled), that cell is also
considered
selected.
JTable provides several additional methods for
querying the state of selection. If there is at least one cell
selected:
getSelectedColumn()
returns the index (in the TreeModel)
of the most recently seleced column (-1 if no
selection
exists).
getSelectedRow()
returns the index (in the TreeModel)
of the most recently selected row (-1 if no
selection
exists).
getSelectedColumns()
and getSelectedRows()
return the TreeModel
indices of all currently selected columns and rows respectively
(int[0] if no selection
exists).
getSelectedColumnCount()
and getSelectedRowCount()
return the current number of selected columns and rows
respectively
(int[0] if no selection
exists).
isColumnSelected()
and isRowSelected()
return a boolean
specifying whether or not the given column or row is currently
selected.
isCellSelected()
returns a boolean
specifying whether or not the cell at the given
TreeModel
row and
column index is selected.
The following methods can be used to
programatically change JTable's
selection, assuming the corresponding selection properties are
enabled:
clearSelection()
unselects all rows, columns and cells.
selectAll()
selects all rows, columns, and cells.
addColumnSelectionInterval()
and addRowSelectionInterval()
allow programmatic selection of a contiguous group of columns and
rows
respectively. Note that these can be called repeatedly to build
of a
multiple-inverval selection if the
MULTIPLE_INTERVAL_SELECTION
mode
is active in the corresponding
selection models.
removeColumnSelectionInterval()
and removeRowSelectionInterval()
allow programmatic de-selection of a contiguous interval of
columns
and rows
respectively. These can also be used repeatedly to affect
mutliple-interval
selections.
setColumnSelectionInterval()
and setRowSelectionInterval()
clear the current column and row selection respectively, and
select
the
specified contiguous interval.
Interestingly, when cell selection is
enabled, JTable
considers the columns and rows containing selected cells selected
themselves
(even though they aren't highlighted). For example, if cells
(1,5) and
(3,6)
are selected with row and column selection enabled and cell
selection
enabled,
getSelectedColumns() will
return {5,6} and getSelectedRows()
will return {1,3}. Oddly enough, those two cells will be
highlighted
and
considered selected by JTable,
along with cells (1,6) and (3,5)! This is due to the fact that
JTable bases cell selection
solely on whether or not both the row and column containing a
cell are
selected. When selected rows and columns
intersect, the cells at the intersection points are considered
selected.
If these same cells are selected when
cell selection is disabled and row and column selection are
enabled,
all cells
in rows 1 and 3, and all cells in columns 5 and 6 will be
considered
selected.
If they are selected with cell selection and only row selection
enabled, all
cells in rows 1 and 3 will be considered selected. Similarly, if
these
two
cells are selected with cell selection and only column selection
enabled, all
cells in columns 5 and 6 will be considered selected. If cell
selection is
not enabled, and row and/or column
selection is enabled, a cell will be considered selected if
either a
column or
row containing it is selected.
Note: Multiple single-cell selections
can be made by holding down the CTRL key and using the mouse for
selection.
Typically we are interested in
determining cell, row, and/or column selection based on a mouse
click.
JTable supports MouseListeners just as
any
other JComponent, and
we can used the getSelectedColumn()
and getSelectedRow()
methods to determine which cell was clicked in
MouseListener's
mouseClicked() method:
myJTable.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// get most recently selected row index
int row = getSelectedRow();
// get most recently selected column index
int column = getSelectedColumn();
if (row == -1 || column == -1)
return; // can't determine selected cell
else
// do something cell-specific
}
});
|
This listener is not very robust
because it will only give us a cell if both a row and column have
recently
been
selected, which in turn can only occur if both row selection and
column
selection is enabled. Thankfully, JTable provides
methods
for
retrieving a row and column index
corresponding to a given Point:
rowAtPoint() and columnAtPoint() (these
will
return -1 if no row
or column is found respectively). Since MouseEvent
carries a
Point specifying the location where the event
occurred, we can use these methods in place of the
getSelectedRow()
and getSelectedColumn() methods. This is
particularly
useful when row,column, and/or cell selection is not enabled.
As with JList, JTable does not directly
support
double-mouse click selections.
However, as we learned in Chapter 10, we can capture a double
click
and
determine which cell was clicked by adding a listener to
JTable
similar to the
following:
myJTable.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
Point origin = e.getPoint();
int row = myJTable.rowAtPoint(origin);
int column =
myJTable.columnAtPoint(origin);
if (row == -1 || column == -1)
return; // no cell found
else
// do something cell-specific
}
}
});
|
Page
1
2
3
4
5
6
7
8
9
10
11
<< NEXT >>
Download Chapter 18| Download Chapter 22]
|