Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 18. (Advanced topics) Tables. Easy for reading, Click here!

Custom Search
Swing Chapter 18. (Advanced topics) Tables. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 1/9 



Next Page (2/9) Next Page

Chapter 18. Tables

Subpages: 1. JTable  
2.
Stocks Table: part I - Basic JTable example 
3.
Stocks Table: part II - Custom renderers 
4. Stocks Table: part III - Data formatting 
5. Stocks Table: part IV - Sorting columns 
6.
Stocks Table: part V - JDBC
7.
Stocks Table: part VI - Column addition and removal
8.
Expense report application
9.
JavaBeans property editor

In this chapter:

  •          JTable
  •          Stocks Table: part I - Basic JTable example
  •          Stocks Table: part II - Custom renderers
  •          Stocks Table: part III - Data formatting
  •          Stocks Table: part IV - Sorting columns
  •          Stocks Table: part V - JDBC
  •          Stocks Table: part VI - Column addition and removal
  •          Expense report application
  •          JavaBeans property editor

18.1  JTable

JTable is extremely useful for the display, navigation, and editing of tabular data. Because of its complex nature, JTable has a whole package devoted just to it: javax.swing.table. This package consists of a set of classes and interfaces which we will review briefly here. In the examples that follow, we construct--in a step-wise fashion--a table-based application used to display stock market data. (In chapters 22 and 26 we enhance this application further to allow printing and print preview, as well as CORBA client-server interaction.) This chapter concludes with an expense report application demonstrating the use of different components as table cell editors and renderers, and the completion of the JavaBeans property editor we started to build in chapter 4.

18.1.1 JTable

class javax.swing.JTable

This class represents Swing's table component and provides a rich API for managing its behavior and appearance. JTable directly extends JComponent and implements the TableModelListener, TableColumnModelListener, ListSelectionListener, CellEditorListener, and the Scrollable interfaces (it is meant to be placed in a JScrollPane). Each JTable has three models: a TableModel, TableColumnModel, and ListSelectionModel. All table data is stored in a TableModel, normally in a 2-dimensional structure such as a 2D array or a Vector of Vectors. TableModel implementations specify how this data is stored, as well as manage the addition, manipulation, and retrieval of this data. TableModel also plays a role in dictating whether or not specific cells can be edited, as well as the data type of each column of data. The location of data in a JTable's TableModel does not directly correspond to the location of that data as displayed by JTable itself. This part is controlled at the lowest level by TableColumnModel.

A TableColumnModel is designed to maintain instances of TableColumn, each of which represents a single column of TableModel data. TableColumn is the class that is responsible for managing the display of a column in the actual JTable GUI. Each TableColumn has an associated cell renderer, cell editor, table header, and a cell renderer for the table header. When a JTable is placed in a JScrollPane these  headers are placed in the scroll pane's COLUMN_HEADER viewport and can be dragged and resized to reorder and change the size of columns. A TableColumn's header renderer is responsible for returning a component used to render the column header, and the cell renderer is responsible for returning a component used to render each cell. As with JList and JTree renderers, these renderers also act as "rubber stamps"API and are not at all interactive. The component returned by the cell editor, however, is completely interactive. Cell renderers are instances of TableCellRenderer and cell editors are instances of CellEditor. If none are explicitly assigned, default versions will be used based on the Class type of the corresponding TableModel colmn data.

TableColumnModel's job is to manage all TableColumns, providing control over order, column selections, and margin size. To support several different modes of selection, TableColumnModel maintains a ListSelectionModel which, as we learned in chapter 10, allows single, single-interval, and multiple-interval selections. JTable takes this flexibility even further by providing functionality to customize any row, column, and/or cell-specific selection schemes we can come up with.

We can specify one of several resizing policies which dictate how columns react when another column is resized, as well as whether or not grid lines between rows and/or columns should appear, margin sizes between rows and columns, selected and unselected cell foreground and background colors, the hieght of rows, and the width of each column on a column-by-column basis.

With tables come two new kinds of events in Swing: TableModelEvent and TableColumnModelEvent. Regular Java events apply to JTable as well. For instance, we can use MouseListeners to process double mouse clicks. ChangeEvents and ListSelectionEvents are also used for communication in TableColumnModel.

Note: Although JTable implements several listener interfaces, it does not provide any methods to register listeners other than those inherited from JComponent. To attach listeners for detecting any of the above events we must first retrieve the appropriate model.

A number of constructors are provided for building a JTable component. We can use the default constructor or pass the table's data and column names each as a separate Vector. We can build an empty JTable with a specified number of rows and columns. We can also pass table data to the constructor as a two-dimensional array of data Objects along with an Object array of column names. Other constructors allow creation of a JTable with specific models. In all cases, if a specific model is not assigned in the constructor, JTable will create default implementations with its protected createDefaultColumnModel(), createDefaultDataModel(), and createDefaultSelectionModel() methods. It will do the same for each TableColumn renderer and editor, as well as its JTableHeader, using  createDefaultEditors(), createDefaultRenderers(), and createDefaultTableHeader().

JTable is one of the most complex Swing components and keeping track of its constituents and how they interact is intially a challenge. Before we begin the step-wise construction of our stocks table application, we must make our way through all of these details. The remainder of this section is devoted to a discussion of the classes and interfaces that underly JTable.

18.1.2  The TableModel interface

abstract interface javax.swing.table.TableModel

Instances of TableModel are responsible for storing a table's data in a 2-dimensional structure such as a 2-dimensional array or a vector of vectors. A set of methods is declared for use in retrieving data from a table's cells. The getValueAt() method should retrieve data from a given row and column index as an Object, and setValueAt() should assign the provided data object to the specified location (if valid). getColumnClass() should return the Class describing the data objects stored in the specified column (used to assign a default renderer and editor for that column), and getColumnName() should return the String name associated with the specified column (often used for that column's header). The getColumnCount() and getRowCount() methods should return the number of contained columns and rows respectively.

Note: The getRowCount() is called frequently by JTable for display purposes and should be designed with efficiency in mind because of this.

The isCellEditable() method should return true if the cell at the given row and column index can be edited. The setValueAt() method should be designed so that if isCellEditable() returns false, the object at the given location will not be updated.

This model supports the attachment of TableModelListeners (see below) which should be notified about changes to this model's data. As expected, methods for adding and removing these listeners are provided, addTableModelListener() and removeTableModelListener(), and implementations are responsible for dispatching TableModelEvents to those registered.

Each JTable uses one TableModel instance which can be assigned/retrieved using JTable's setModel() and getModel() methods respectively.

18.1.3  AbstractTableModel

abstract class javax.swing.table.AbstractTableModel

AbstractTableModel is an abstract class implementing the TableModel interface. It provides default code for firing TableModelEvents with the fireTableRowsDeleted(), fireTableCellUpdated(), and fireTableChanged() methods. It also manages all registered TableModelListeners in an EventListenerList (see chapter 2).

The findColumn() method searches for the index of a column with the given String name. This search is performed in a linear fashion (referred to as "naive" in the documentation) and should be overriden for large table models for more efficient searching.

Three methods need to be implemented in concrete sub-classes: getRowCount(), getColumnCount() and getValueAt(int row, int column), and we are expected to use this class as a base for building our own TableModel implementations (rather than DefaultTableModel--see below).

18.1.4  DefaultTableModel

class javax.swing.tableDefaultTableModel

DefaultTableModel is the default concrete TableModel implementation used by JTable when no model is specified in the constructor. It uses a Vector of Vectors to manage its data, which is one major reason why extending AbstractTableModel is often more desirable (because AbstractTableModel allows complete control over how data storage and manipulation is implemented). This Vector can be assigned with the overloaded setDataVector() method, and retrieved with the getDataVector() method. Internally, two overloaded, protected convertToVector() methods are used for converting Object arrays to Vectors when inserting rows, columns, or assigning a new data Vector. Methods for adding, inserting, removing, and moving columns and rows of data are also provided.

Note: The position of a row or column in the model does not correspond to JTable's GUI representation of that row or column. Rather, this representation is performed instances of TableColumn which map to specific model columns. When a TableColumn is moved in the GUI, the associated data in the TableModel model stays put, and vice versa (see below).

Along with the TableModelEvent functionality inherited from AbstractTableModel, this class implements three new event-dispatching methods, each taking a TableModelEvent as parameter: newDataAvailable(), newRowsAdded(), and rowsRemoved(). The newRowsAdded() method ensures that new rows (see discussion of TableModelEvent below) have the correct number of columns by either removing excess elements, or using null for each missing cell. If null is passed to any of these methods they will construct and fire a default TableModelEvent which assumes that all table model data has changed.

18.1.5  TableColumn

class javax.swing.table.TableColumn

TableColumn is the basic building block of JTable's visual representation, and provides the main link between the JTable GUI and its model. Note that TableColumn does not extend java.awt.Component, and is thus not a component. Rather, it acts more like a model that maintains all the properties of a column displayed in a JTable. An instance of TableColumn represents a specific column of data stored in a TableModel. TableColumn maintains the index of the TableModel column it represents as property modelIndex. We can get/set this index with the getModelIndex() and setModelIndex() methods. It is important to remember that the position of a TableColumn graphically in JTable does not at all correspond to the corresponding TableModel column index.

A TableColumn is represented graphically by a column header renderer, cell renderer, and optionally a cell editor. The renderers must be instances of TableCellRenderer, and the editor must be an instance of TabeCellEditor (see below). A column's header is rendered by a component stored as the headerRenderer property. By default this is an instance of DefaultTableCellRenderer (a JLabel with a beveled border--see below) and is created with TableColumn's protected createDefaultHeaderRenderer() method. This renderer simply renders the String returned by the toString() method of the Object referred to by the headerValue property. The header renderer and value can be assigned/retrieved with setHeaderRenderer()/getHeaderRenderer() and setHeaderValue()/getHeaderValue() methods respectively. Often headerValue directly corresponds to the column name retrived using TableModel's getColumnName() method. If headerValue is not explicitly set it defaults to null.

The column cell renderer and editor also default to null, and unless they are explicitly specified using setCellRenderer() or setCellEditor(), are automatically assigned based on the Class type of the data stored in the associated column in the TableModel (retreived using TableModel's getColumnClass() method). Explicity specified renderers and editors are referred to as column-based, whereas those determined by data type are referred to as class-based (we will discuss renderers and editors in more detail below).

Each TableColumn has an identifier property which also defaults null. This property can be assigned/retrieved using typical set/get accessors, and the getIdentifier() method will return the headerValue property if identifier is null. When searching for a TableColumn by name (using TableColumnModel's getColumnIndex() method--see below--or JTable's getColumn() method), the given Object will be compared, using Object's equals() method, to each TableColumn identifier. Since it is possible that more than one TableColumn will use the same identifier, the first match is returned as the answer.

TableColumn maintains properties minWidth, maxWidth, and width. The first two specify the minimum and maiximum allowable widths for column rendering, and the width property stores the current width. Each can be retrieved and assigned with typical get/set methods: getMinWidth()/setMinWidth(), getMaxWith()/setMaxWidth(), and getWidth()/setWidth(). minWidth defaults to 15, maxWidth defaults to Integer.MAX_VALUE, and width defaults to 75.  When a JTable is resized it will try to maintain its width, and will never exceeed its maximum or shrink smaller than its minimum.

Note: All other visual aspects of each column are controlled by either JTable or TableColumnModel (see below).

TableColumn also maintains an isResizable property, specifying whether or not its width can be changed by the user (this does not apply to programmatic calls to setWidth()). (We will discuss resizing in more detail below).

An interesting and rarely used property maintained by TableColumn, resizedPostingDisabledCount, is used to enable/disable the posting of PropertyChangeEvents when a TableColumn's width changes. This is an int value that is incremented on each call to disableResizedPosting(), and decremented on each call to enableResizedPosting(). Events will only be fired if this value is less than or equal to 0. The logic behind this is that if two separate sources both disable resize event posting, then two calls should be required to re-enable it.

Big Alert! As of Java 2 FCS the resizedPostingDisabledCount property is not actually used anywhere and does not play a role in PropertyChangeEvent firing.

TableColumn fires PropertyChangeEvents when any of the width, cellRenderer, headerRenderer, or headerValue bound properties change. Thus we can add and remove PropertyChangeListeners to be notified of these changes. The corresponding property names are COLUMN_WIDTH_PROPERTYCOLUMN_RENDERER_PROPERTY, HEADER_RENDERER_PROPERTY,  and HEADER_VALUE_PROPERTY.

18.1.6  The TableColumnModel interface

abstract interface javax.swing.table.TableColumnModel

This model is designed to maintain a JTable's TableColumns, and provides control over column selections and margin size. TableColumnModel controls how JTable displays its TableModel data. The addColumn() method should append a given TableColumn to the end of the structure used to maintain them (usually a Vector), removeColumn() should remove a given TableColumn, and moveColumn() should change the location of a given TableColumn within that structure.

Note: When creating a JTable, if no TableColumnModel is specified, one will automatically be constructed for us containing TableColumns displaying TableModel data in the same order it appears in the model. This will only occur of JTable's autoCreateColumnsFromModel property is set true, which it is by default. This is very helpful, but has the often undesirable side-effect of completely rebuilding the TableColumnModel whenever TableModel changes. Thus it is common to set this property to false once a JTable has been created or after a new TableModel is assigned.

Unlike the location of a column in TableModel implementations, the index of a TableColumn in a TableColumnModel's storage structure directly corresponds to its position in the JTable GUI. Note that the moveColumn() method is called whenever the user drags a column to a new position.

The getColumnCount() method should return the number of TableColumns currently maintained, getColumns() should return an Enumeration of all contained TableColumns, and getColumn() returns the TableColumn at the given index. The getColumnIndex() method should return the index of the TableColumn whose identifier property is equal to (using Object's equals() method) the given Object. getColumnIndexAtX() should return the index of the TableColumn at the given x-corrdinate in table-space (if getColumnIndexAtX() is passed a coordinate that maps to the margin space between adjacent columns, or any x-coordinate that does not correspond to a table column, it will return -1). setColumnMargin() and getColumnMargin() should allow assignment and retrieval of an int value for use as the margin space on each side of each table column. The getTotalColumnWidth() should return the sum of the current width of all TableColumns including all margin space.

Note: The margin size does not correspond to the width of the separating grid lines between columns in JTable. In fact, the width of these lines is always 1, and cannot be changed without customizing JTable's UI delegate.

TableColumnModel declares methods for controlling the selection of its TableColumns, and allows assignment and retrieval of a ListSelectionModel implementation used to store information about the current column selection with the methods setSelectionModel() and getSelectionModel(). The setColumnSelectionAllowed() method is intended to turn on/off column selection capabilities, and getColumnSelectionAllowed() should return a boolean specifying whether slection is currently allowed or not. For convenience, JTable's setColumnSelectionAllowed() method delegates its traffic to the method of the same signature in this interface.

TableColumnModel also declares support for TableColumnModelListeners (see below). TableColumnModel implementations are expected to fire a TableColumnModelEvent whenever a TableColumn is added, removed, or moved, a ChangeEvent whenever margin size is changed, and a ListSelectionEvent whenever a change in column selection occurs.

18.1.7  DefaultTableColumnModel

class javax.swing.table.DefaultTableColumnModel

This class is the concrete default implementation of the TableColumnModel interface used by JTable when none is specifically assigned or provided at construction time. All TableColumnModel methods are implemented as expected (see above), and the following protected methods are provided to fire events: fireColumnAdded(), fireColumnRemoved(), fireColumnMoved(), fireColumnSelectionChanged(), and fireColumnMarginChanged(). A valueChanged() method is provided to listen for column selection changes and fire a ListSelectionEvent when necessary. And a propertyChanged() method is used to update the totalColumnWidth property when the width of a contained TableColumn changes.

18.1.8  The TableCellRenderer interface

abstract interface javax.swing.table.TableCellRenderer

This interface describes the renderer used to display cell data in a TableColumn. Each TableColumn has an associated TableCellRender which can be assigned/retrieved with the setCellRenderer()/getCellRenderer() methods. The getTableCellRendererComponent() method is the only method declared by this interface, and is expected to return a Component that will be used to actually render a cell. 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.

boolean hasFocus: whether or not the given cell has the focus (true if it was clicked last).

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 JLabel subclass that paints a rectangle in the given color. This method can be used to return different renderer components on a column, row, or cell-specific basis, and is similar to JTree's TreeCellRenderer getTreeCellRendererComponent() method. As with JTree and JList, the renderer component returned acts as a "rubber stamp"API used strictly for display purposes.

Note: the row and column parameters refer to the location of data in the TableModel, not a cell location in the TableColumnModel.

When JTable's UI delegate repaints a certain region of a table it must query that table to determine the renderer to use for each cell that it needs to repaint. This is accomplished through JTable's getCellRenderer() method which takes row and column parameters, and returns the component returned by the getTableCellRendererComponent() method of the TableCellRenderer assigned to the appropriate TableColumn. If there is no specific renderer assigned to that TableColumn (recall that this is the case by default), the TableModel's getColumnClass() method is used to recursively determine an appropriate renderer for the given data type. If there is no specific class-based renderer specified for a given class, getColumnClass() searches for one corresponding to the super-class. This process will, in the most generic case, stop at Object, for which a DefaultTabelCellRenderer is used (see below).

A DefaultTreeCellRenderer is also used if the class is of type Number (subclasses are BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, and Short) or Icon. If the type happens to be a Boolean, a JCheckBox is used. We can specify additional class-based renderers with JTable's setDefaultRenderer() method. Remember that class-based renderers will only be used if no column-based renderer has been explicitly assigned to the TableColumn containing the given cell.

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  retrieved 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.15            TableColumnModelEvent

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.

UI Guideline : 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.

UI Guideline : 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 row 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

      }

    }

  });

18.1.18            Column Width and Resizing

When a column's width increases, JTable must decide how other columns will react. One or more columns must shrink. Similarly, when a column's width decreases, JTable must decide how other columns will react to the newly available amount of space. JTable's autoResizeMode property can take on any of five different values that handle these cases differently:

JTable.AUTO_RESIZE_ALL_COLUMNS: All columns gain or lose an equal amount of space corresponding the width lost or gained by the resizing column.

JTable.AUTO_RESIZE_LAST_COLUMN: The right-most column shrinks or grows in direct correspondence with the amount of width lost or gained from the column being resized. All other colmns are not affected.

JTable.AUTO_RESIZE_NEXT_COLUMN: The column to the immediate right of the column being resized shrinks or grows in direct correspondence with the amount of width lost or gained from the resizing column. All other colmns are not affected.

JTable.AUTO_RESIZE_OFF: resizing only affects the column being sized. All columns to the right of the column being resized are shifted right or left accordingly while maintaining their current sizes. Columns to the left are not affected.

JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS: All columns to the right of the column being resized gain or lose an equal amount of space corresponding the width lost or gained by the resizing column. Columns to the left are not affected.

TableColumn's width defaults to 75. Its minimum width defaults to 15 and its maximum width defaults to Integer.MAX_VALUE. When a JTable is first displayed it attempts to size each TableColumn according to its width property. If that table's autoResizeMode property is set to AUTO_RESIZE_OFF this will occur successfully. Otherwise, TableColumns are adjusted according to the current autoResizeMode property.

A TableColumn will never be sized larger than its maximum width or smaller than its minimum. For this reason it is possible that a JTable will occupy a larger or smaller area than that available (i.e. visible in the parent JScrollPane's main viewport), which may result in part of the table being clipped from view. If a table is contained in a JScrollPane and it occupies more than the available visible width, a horizontal scrollbar will be presented.

At any time we can call TableColumn's sizeWidthToFit() method to resize a column to occupy a width corresponding to the preferred width of its table header renderer. This is often used in assigning minimum widths for each TableColumn. JTable's sizeColumnsToFit() method takes an int parameter specifying the index of the TableColumn to act as the source of a resize in an attempt to make all columns fit within the available visible space. Also note that TableColumnModel's getTotalColumnWidth() method will return the sum of the current width of all TableColumns including all margin space.

We can specify the amount of empty space between rows with JTable's setRowMargin() method, and we can assign all rows a specific height with setRowHeight(). JTable's setIntercellSpacing() method takes a Dimension instance and uses it to assign a new width and height to use as margin space between cells (this method will redisplay the table it is invoked on after all sizes have been changed).

18.1.19            JTable Appearance

We can change the background and foreground colors used to highlight selected cells by setting the  selectedBackground and setSelectedForeground properties. The default colors used for each TableColumn's table header renderer are determined from the current JTableHeader's background and foreground colors (recall that JTableHeader extends JComponent).

We can turn on and off horizontal and vertical grid lines (which are always have a thickness of 1 pixel) by changing the showHorizontalLines and showVerticalLines. properties. The showGrid property will overpower these properties when set with setShowGrid() becauuse this method reasigns them to the specified value. So setShowGrid() turns on/off both vertical and horizontal lines as specified. The gridColor property specifies the Color to use for both vertical and horizontal grid lines. setGridColor() will assign the specified value to this property and then repaint the whole table.

UI Guideline : Visual Noise Grid Lines add visual noise to the display of a table. Removing some of them can aid the reading of the table data. If you intend the reader to read rows across then switch off the vertical grid lines. If you have columns of figures for example, then you might prefer to switch off the horizontal grid lines, making the columns easier to read.

When switching off the horizontal grid lines on the table, you may wish to use the column cell renderer to change the background color of alternate rows on the table to ease the reading of rows. Using this combination of visual techniques, grid lines to distinguish columns and colour to distinguish rows, helps guide the reader to interpret data as required.

18.1.20            JTable scrolling

JTable implements the Scrollable interface (see 7.1.4) and is intended to be placed in a JScrollPane. JTableHeaders will not be displayed otherwise, disabling any column resize capabilities. Among the required Scrollable methods, JTable implements getScrollableTracksViewportWidth() to return true, which forces JTable to attempt to size itself horizontally to fit within the current scroll pane viewport width. getScrollableTracksViewportHeight(), however, returns false as it is most common for tables to be vertically scrolled but not horizontally scrolled. Horizontal scrolling is often awkward and we advise avoiding it whenever possible.

JTable's vertical block increment is the number of visible rows less one, and its vertical unit increment is the current row height. The horizontal block inrement is the width of the viewport, and the horizontal unit increment defaults to 100.

UI Guideline : small grids, no column headers If you have a requirement to show two or three pieces of data, grouped and aligned together, consider using a JTable without JScrollPane. This gives you a small grid which is already aligned and neat and tidy for display without column headers.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy