You are on page 1of 8

The table in SimpleTableDemo.

java declares the column names in a String array: String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"}; Its data is initialized and stored in a two-dimensional Object array: Object[][] data = { {"Kathy", "Smith", "Snowboarding", new Integer(5), new Boolean(false)}, {"John", "Doe", "Rowing", new Integer(3), new Boolean(true)}, {"Sue", "Black", "Knitting", new Integer(2), new Boolean(false)}, {"Jane", "White", "Speed reading", new Integer(20), new Boolean(true)}, {"Joe", "Brown", "Pool", new Integer(10), new Boolean(false)} }; Then the Table is constructed using these data and columnNames: JTable table = new JTable(data, columnNames); There are two JTable constructors that directly accept data (SimpleTableDemo use s the first): JTable(Object[][] rowData, Object[] columnNames) JTable(Vector rowData, Vector columnNames) The advantage of these constructors is that they are easy to use. However, these constructors also have disadvantages: They automatically make every cell editable. They treat all data types the same (as strings). For example, if a table column has Boolean data, the table can display the data in a check box. However, if you use either of the two JTable constructors listed previously, your Boolean data is displayed as a string. You can see this difference in the Vegetarian column o f the previous figure. They require that you put all of the table's data in an array or vector, which m ay not be appropriate for some data. For example, if you are instantiating a set of objects from a database, you might want to query the objects directly for th eir values, rather than copying all their values into an array or vector.

Adding a Table to a Container Here is typical code for creating a scroll pane that serves as a container for a table: JScrollPane scrollPane = new JScrollPane(table); table.setFillsViewportHeight(true); The two lines in this snippet do the following: The JScrollPane constructor is invoked with an argument that refers to the table object. This creates a scroll pane as a container for the table; the table is a utomatically added to the container. JTable.setFillsViewportHeight is invoked to set the fillsViewportHeight property . When this property is true the table uses the entire height of the container, even if the table doesn't have enough rows to use the whole vertical space. This

makes it easier to use the table as a drag-and-drop target. The scroll pane automatically places the table header at the top of the viewport . The column names remain visible at the top of the viewing area when the table data is scrolled. If you are using a table without a scroll pane, then you must get the table head er component and place it yourself. For example: container.setLayout(new BorderLayout()); container.add(table.getTableHeader(), BorderLayout.PAGE_START); container.add(table, BorderLayout.CENTER); Setting and Changing Column Widths By default, all columns in a table utomatically fill the entire width narrower (which might happen when ble), all the column widths change start out with equal width, and the columns a of the table. When the table becomes wider or the user resizes the window containing the ta appropriately.

When the user resizes a column by dragging its right border, then either other c olumns must change size, or the table's size must change. By default, the table' s size remains the same, and all columns to the right of the drag point resize t o accommodate space added to or removed from the column to the left of the drag point. To customize initial column widths, you can invoke setPreferredWidth on each of your table's columns. This sets both the preferred widths of the columns and the ir approximate relative widths. For example, adding the following code to Simple TableDemo makes its third column bigger than the other columns: TableColumn column = null; for (int i = 0; i < 5; i++) { column = table.getColumnModel().getColumn(i); if (i == 2) { column.setPreferredWidth(100); //third column is bigger } else { column.setPreferredWidth(50); } } As the preceding code shows, each column in a table is represented by a TableCol umn object. TableColumn supplies getter and setter methods for the minimum, pref erred, and maximum widths of a column, as well as a method for getting the curre nt width. For an example of setting cell widths based on an approximation of the space needed to draw the cells' contents, see the initColumnSizes method in Tab leRenderDemo.java. When the user explicitly resizes columns, the columns' preferred widths are set such that the user-specified sizes become the columns' new current widths. Howev er, when table itself is resized typically because the window has resized ; the c olumns' preferred widths do not change. Instead, the existing preferred widths a re used to calculate new column widths to fill the available space. You can change a table's resize behavior by invoking setAutoResizeMode. User Selections In its default configuration, a table supports a selection that consists of one or more rows. The user can select a contiguous range of rows or an arbitrary set of rows. The last cell that the user indicated gets a special indication; in th e Metal look and feel, the cell is outlined. This cell is known as the lead sele ction; it is sometimes called "the cell with the focus" or "the current cell".

The user uses the mouse and/or keyboard to make selections, as described in the following table: Operation Mouse Action Keyboard Action Select single row. Click. Up Arrow or Down Arrow. Extend contiguous selection. Shift-Click or Drag over rows. Shift-Up Arrow o r Shift-Down Arrow. Add row to selection/toggle row selection. Control-Click Move lead select ion with Control-Up Arrow or Control-Down Arrow, then use Space Bar to add to se lection or Control-Space Bar to toggle row selection. To see how selections work, click the Launch button to run TableSelectionDemo us ing Java Web Start (download JDK 6 or later). Or, to compile and run the example yourself, consult the example index. This example program presents the familiar table, and allows the user to manipul ate certain JTable options. There is also a text pane that logs selection events . In the screenshot below, a user has run the program, clicked in the first row, t hen control-clicked in the third row. Notice the outline around the last cell cl icked; this is how the Metal look and feel highlights the lead selection. Under "Selection Mode" there are a set of radio buttons. Click the one labelled "Single Selection". Now you can only select one row at a time. If you click on t he "Single Interval Selection" radio button, you can select a set of rows that m ust be contiguous. All of the radio buttons under "Selection Mode" invoke JTable.setSelectionMode. This method takes a single argument, which must be one of the following constant s defined in javax.swing.ListSelectionModel: MULTIPLE_INTERVAL_SELECTION, SINGLE _INTERVAL_SELECTION, and SINGLE_SELECTION. Returning to TableSelectionDemo, notice the three option checkboxes under "Selec tion Options." Each of checkbox controls the state of a boolean bound variable d efined by JTable: "Row Selection" controls rowSelectionAllowed which has setter method setRowSelec tionAllowed and getter method getRowSelectionAllowed. When this bound property i s true (and the columnSelectionAllowed property is false), the user can select b y row. "Column Selection" controls columnSelectionAllowed which has setter method setCo lumnSelectionAllowed and getter method getColumnSelectionAllowed. When this boun d property is true (and the rowSelectionAllowed bound property is false), the us er can select by column. "Cell Selection" controls cellSelectionEnabled, which has setter method setCellS electionEnabled and getter method getCellSelectionEnabled. When this bound prope rty is true, the user can select a single cell or rectangular block of cells. NOTE: JTable uses a very simple concept of selection, managed as an intersection of ro ws and columns. It was not designed to handle fully independent cell selections. If you clear all three check boxes (setting all three bound properties to false) , there is no selection; only the lead selection is shown. You may notice that the "Cell Selection" checkbox is disabled in multiple interv al selection mode. This is because cell selection is not supported in this mode in the demo. You can specify selection by cell in multiple interval selection mo

de, but the result is a table that does not produce useful selections. You may also notice the others. This is exactly the same as ree bound variables that changing any of the three selection options can affect because allowing both row selection and column selection is enabling cell selection. JTable automatically updates the th as necessary to keep them consistent.

NOTE: Setting cellSelectionEnabled to a value has the side effect of also setting both rowSelectionEnabled and columnSelectionEnabled to that value. Setting both rowS electionEnabled and columnSelectionEnabled to a value has the side effect of als o setting cellSelectionEnabled to that value. Setting rowSelectionEnabled and co lumnSelectionEnabled to different values has the side effect of also setting cel lSelectionEnabled to false. To retrieve the current selection, use JTable.getSelectedRows which returns an a rray of row indexes, and JTable.getSelectedColumns which returns an array of col umn indexes. To retrieve the coordinates of the lead selection, refer to the sel ection models for the table itself and for the table's column model. The followi ng code formats a string containing the row and column of the lead selection: String.format("Lead Selection: %d, %d. ", table.getSelectionModel().getLeadSelectionIndex(), table.getColumnModel().getSelectionModel().getLeadSelectionIndex()); User selections generate a number of events. For information on these, refer to How to Write a List Selection Listener in the Writing Event Listeners lesson. NOTE: Selection data actually describes selected cells in the "view" (table data as it appears after any sorting or filtering) rather than in the table model. This di stinction does not matter unless your viewed data has been rearranged by sorting , filtering, or user manipulation of columns. In that case, you must convert sel ection coordinates using the conversion methods described in Sorting and Filteri ng. Creating a Table Model Every table object uses a table model object to manage the actual table data. A table model object must implement the TableModel interface. If the programmer do es not provide a table model object, JTable automatically creates an instance of DefaultTableModel. This relationship is illustrated below. The JTable constructor used by SimpleTableDemo creates its table model with code like this: new AbstractTableModel() { public String getColumnName(int col) { return columnNames[col].toString(); } public int getRowCount() { return rowData.length; } public int getColumnCount() { return columnNames.length; } public Object getValueAt(int row, int col) { return rowData[row][col]; } public boolean isCellEditable(int row, int col) { return true; } public void setValueAt(Object value, int row, int col) { rowData[row][col] = value; fireTableCellUpdated(row, col);

} } As the preceding code shows, implementing a table model can be simple. Generally , you implement your table model in a subclass of the AbstractTableModel class. Your model might hold its data in an array, vector, or hash map, or it might get the data from an outside source such as a database. It might even generate the data at execution time. This table is different from the SimpleTableDemo table in the following ways: TableDemo's custom table model, even though it is simple, can easily determine t he data's type, helping the JTable display the data in the best format. SimpleTa bleDemo's automatically created table model, on the other hand, does not know th at the # of Years column contains numbers (which should generally be right align ed and have a particular format). It also does not know that the Vegetarian colu mn contains boolean values, which can be represented by check boxes. The custom table model implemented in TableDemo does not let you edit the name c olumns; it does, however, let you edit the other columns. In SimpleTableDemo, al l cells are editable. See below the code taken from TableDemo.java that is different from the SimpleTa bleDemo.java. Bold font indicates the code that makes this table's model differe nt from the table model defined automatically for SimpleTableDemo. public TableDemo() { ... JTable table = new JTable(new MyTableModel()); ... } class MyTableModel extends AbstractTableModel { private String[] columnNames = ...//same as before... private Object[][] data = ...//same as before... public int getColumnCount() { return columnNames.length; } public int getRowCount() { return data.length; } public String getColumnName(int col) { return columnNames[col]; } public Object getValueAt(int row, int col) { return data[row][col]; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } /* * Don't need to implement this method unless your table's * editable. */ public boolean isCellEditable(int row, int col) { //Note that the data/cell address is constant,

//no matter where the cell appears onscreen. if (col < 2) { return false; } else { return true; } } /* * Don't need to implement this method unless your table's * data can change. */ public void setValueAt(Object value, int row, int col) { data[row][col] = value; fireTableCellUpdated(row, col); } ... } Listening for Data Changes A table model can have a set of listeners that are notified whenever the table d ata changes. Listeners are instances of TableModelListener. In the following exa mple code, SimpleTableDemo is extended to include such a listener. New code is i n bold. import javax.swing.event.*; import javax.swing.table.TableModel; public class SimpleTableDemo ... implements TableModelListener { ... public SimpleTableDemo() { ... table.getModel().addTableModelListener(this); ... } public void tableChanged(TableModelEvent e) { int row = e.getFirstRow(); int column = e.getColumn(); TableModel model = (TableModel)e.getSource(); String columnName = model.getColumnName(column); Object data = model.getValueAt(row, column); ...// Do something with the data... } ... } Firing Data Change Events In order to fire data change events the table model must know how to construct a TableModelEvent object. This can be a complex procedure, but is already impleme nted in DefaultTableModel. You can either allow JTable to use its default instan ce of DefaultTableModel, or create your own custom subclass of DefaultTableModel . If DefaultTableModel is not a suitable base class for your custom table model cl ass, consider subclassing AbstractTableModel. This class implements a simple fra mework for constructing TableModelEvent objects. Your custom class simply needs to invoke one the following AbstractTableModel methods each time table data is c hanged by an external source.

Method Change fireTableCellUpdated Update of specified cell. fireTableRowsUpdated Update of specified rows fireTableDataChanged Update of entire table (data only). fireTableRowsInserted New rows inserted. fireTableRowsDeleted Existing rows Deleted fireTableStructureChanged Invalidate entire table, both data and structure . Concepts: Editors and Renderers Before you go on to the next few tasks, you need to understand how tables draw t heir cells. You might expect each cell in a table to be a component. However, fo r performance reasons, Swing tables are implemented differently. Instead, a single cell renderer is generally used to draw all of the cells that contain the same type of data. You can think of the renderer as a configurable i nk stamp that the table uses to stamp appropriately formatted data onto each cel l. When the user starts to edit a cell's data, a cell editor takes over the cell , controlling the cell's editing behavior. For example, each cell in the # of Years column in TableDemo contains Number dat a specifically, an Integer object. By default, the cell renderer for a Number-co ntaining column uses a single JLabel instance to draw the appropriate numbers, r ight-aligned, on the column's cells. If the user begins editing one of the cells , the default cell editor uses a right-aligned JTextField to control the cell ed iting. To choose the renderer that displays the cells in a column, a table first determ ines whether you specified a renderer for that particular column. If you did not , then the table invokes the table model's getColumnClass method, which gets the data type of the column's cells. Next, the table compares the column's data typ e with a list of data types for which cell renderers are registered. This list i s initialized by the table, but you can add to it or change it. Currently, table s put the following types of data in the list: Boolean rendered with a check box. Number rendered by a right-aligned label. Double, Float same as Number, but the object-to-text translation is performed by a NumberFormat instance (using the default number format for the current locale ). Date rendered by a label, with the object-to-text translation performed by a Dat eFormat instance (using a short style for the date and time). ImageIcon, Icon rendered by a centered label. Object rendered by a label that displays the object's string value. Cell editors are chosen using a similar algorithm. Remember that if you let a table create its own model, it uses Object as the typ e of every column. To specify more precise column types, the table model must de fine the getColumnClass method appropriately, as demonstrated by TableDemo.java. Keep in mind that although renderers determine how each cell or column header lo oks and can specify its tool tip text, a renderer does not handle events. If you need to pick up the events that take place inside a table, the technique you us e varies by the sort of event you are interested in: Situation How to Get Events To detect events from a cell that is being edited... Use the cell editor (or register a listener on the cell editor). To detect row/column/cell selections and deselections... Use a selection

listener as described in Detecting User Selections. To detect mouse events on a column header... Register the appropriate type of mouse listener on the table's JTableHeader object. (See TableSorter.java for an example.) To detect other events... Register the appropriate listener on the JTable object. The next few sections tell you how to customize display and editing by specifyin g renderers and editors. You can specify cell renderers and editors either by co lumn or by data type.