Sun Java Solaris Communities My SDN Account Join SDN
 
Swing Introduction

Swing

 

Bookshelf Index


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]