The JFC Swing Tutorial: A Guide to Constructing GUIs (2nd Edition)
< Day Day Up > |
With the JTable [157] class you can display tables of data, optionally allowing the user to edit the data. JTable doesn't contain or cache data; it's simply a view of your data. (See Figure 69.) The rest of this section tells you how to accomplish some common table- related tasks . [157] JTable API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTable.html. Figure 69. A typical table displayed within a scroll pane.
Creating a Simple Table
Figure 70 shows a simple table that catalogs personal tidbits about five people. Figure 70. The SimpleTableDemo application.
Try This:
Here's the code that implements the table in SimpleTableDemo : String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"}; Object[][] data = { {"Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(false)}, {"Alison", "Huml", "Rowing", new Integer(3), new Boolean(true)}, {"Kathy", "Walrath", "Knitting", new Integer(2), new Boolean(false)}, {"Sharon", "Zakhour", "Speed reading", new Integer(20), new Boolean(true)}, {"Philip", "Milne", "Pool", new Integer(10), new Boolean(false)} }; JTable table = new JTable(data, columnNames); The SimpleTableDemo example uses one of two JTable constructors that directly accept data:
The advantage of these constructors is that they're easy to use. However, these constructors also have disadvantages:
If you want to get around these restrictions, you need to implement your own table model, as described in Creating a Table Model (page 394). Adding a Table to a Container
It's easy to put a table in a scroll pane. You need just one or two lines of code: JScrollPane scrollPane = new JScrollPane(table); table.setPreferredScrollableViewportSize(new Dimension(500, 70)); The scroll pane automatically gets the table's header, which displays the column names , and puts it on top of the table. Even when the user scrolls down, the column names remain visible at the top of the viewing area. The scroll pane also tries to make its viewing area the same as the table's preferred viewing size. The previous code snippet sets the table's preferred viewing size with the setPreferredScrollableViewportSize method. If you're using a table without a scroll pane, then you must get the table header 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 start out with equal width, and the columns automatically fill the entire width of the table. When the table becomes wider or narrower (which might happen when the user resizes the window containing the table), all the column widths change appropriately. When the user resizes a column by dragging its right border, then either other columns 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 to accommodate space added to or removed from the column to the left of the drag point. Figures 71 through 73 illustrate the default resizing behavior. Figure 71. Initially, the columns have equal width.
Figure 73. When the entire table is resized, all the columns are resized.
Figure 72. When the user resizes a column, some of the other columns must adjust size for the table to stay the same size.
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 their approximate relative widths. For example, adding the following code to SimpleTableDemo 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); //sport column is bigger } else { column.setPreferredWidth(50); } } As the preceding code shows, each column in a table is represented by a TableColumn [159] object. TableColumn supplies getter and setter methods for the minimum, preferred, and maximum widths of a column, as well as a method for getting the current width. For an example of setting cell widths based on the actual amount of space needed to draw the cells' contents, see the initColumnSizes method in TableRenderDemo.java . [160] [159] TableColumn API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/TableColumn.html. [160] You can find TableRenderDemo.java here: JavaTutorial/uiswing/components/example-1dot4/TableRenderDemo.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. However, when the table itself is resizedtypically because the window has resizedthe columns' preferred widths do not change. Instead, the existing preferred widths are used to calculate new column widths to fill the available space. You can change a table's resize behavior by invoking the setAutoResizeMode method. The method's argument should have one of these values (defined as constants in JTable ): AUTO_RESIZE_SUBSEQUENT_COLUMNS
The default. In addition to resizing the column to the left of the drag point, adjusts the sizes of all columns to the right of the drag point. AUTO_RESIZE_NEXT_COLUMN
Adjusts only the columns immediately to the left and right of the drag point. AUTO_RESIZE_OFF
Adjusts the table size instead. Detecting User Selections
The following code snippet shows how to detect when the user selects a table row. By default, a table allows the user to select multiple rowsnot columns or individual cellsand the selected rows need not be next to each other. Using the setSelectionMode method, the following code specifies that only one row at a time can be selected. table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ... //Ask to be notified of selection changes. ListSelectionModel rowSM = table.getSelectionModel(); rowSM.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { //Ignore extra messages. if (e.getValueIsAdjusting()) return; ListSelectionModel lsm = (ListSelectionModel)e.getSource(); if (lsm.isSelectionEmpty()) { ...//no rows are selected } else { int selectedRow = lsm.getMinSelectionIndex(); ...//selectedRow is selected } } }); The code is from SimpleTableSelectionDemo.java . [161] SimpleTableSelectionDemo also has code (not included in the preceding snippet) that changes the table's selection orientation. By changing a couple of boolean values, you can make the table allow either column selections or individual cell selections, instead of row selections. [161] You can find SimpleTableSelectionDemo.java here: JavaTutorial/uiswing/components/example-1dot4/SimpleTableSelectionDemo.java . For more information and examples of implementing selection, see How to Write a List Selection Listener (page 685) in Chapter 10. Creating a Table Model
As Figure 74 shows, every table gets its data from an object that implements the Table-Model [162] interface. [162] TableModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/TableModel.html. Figure 74. Every table gets its data from TableModel .
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 [163] class. [163] AbstractTableModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/AbstractTableModel.html. 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.
Figure 75. The table implemented by the TableDemo application.
This table is different from the SimpleTableDemo table in the following ways:
Below is the code from TableDemo.java that is different from the code in SimpleTableDemo.java . Bold font indicates the code that makes this table's model different 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); } ... } Detecting Data Changes
A table and its model automatically detect whenever the user edits the table's data. However, if the data changes for another reason, you must take special steps to notify the table and its model of the data change. Also, if you don't implement a table model, as in SimpleTableDemo , then you must take special steps to find out when the user edits the table's data. An example of updating a table's data without directly editing it is in the BINGO application. The BINGO application, presented in "Putting It All Together," [165] has a table that displays some information about each user who is signed up to play the game. When a new user signs up to play BINGO, the table needs to add a new row for that user. More precisely, the table model needs to get the data for the new user, and then the table model needs to tell the table to display the new data. [165] The BINGO! game is featured in The Java Tutorial trail "Putting It All Together," available online and on the CD at: JavaTutorial/together/bingo/index.html . To notify the table model about a new user, the BINGO application invokes the table model's updatePlayer method. You can see the code for that method in PlayerInfoModel , [166] which contains the implementation of the table model. The updatePlayer method records the new user's data and fires a table-model event. Because every table listens for table-model events from its model, the user-information table automatically detects the change and displays the new data. [166] You can find all the code needed to play BINGO! online and on the CD at: JavaTutorial/together/bingo/letsplay.html#download . To fire the table-model event, the model invokes the fireTableRowsInserted method, which is defined by the AbstractTableModel class. Other fire Xxxx methods that AbstractTableModel defines are fireTableCellUpdated , fireTableChanged , fire-TableDataChanged , fireTableRowsDeleted , fireTableRowsInserted , fireTableRows-Updated , and fireTableStructureChanged . If you have a class such as SimpleTableDemo that isn't a table or table model, but needs to react to changes in a table model, then you need to do something special to find out when the user edits the table's data. Specifically , you need to register a TableModelListener [167] on the table model. Adding the bold code in the following snippet makes SimpleTableDemo react to table data changes. [167] TableModelListener API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/event/TableModelListener.html. 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... } ... } Concepts: Editors and Renderers
Before you go on to the next few tasks, you need to understand how tables draw their cells. You might expect each cell in a table to be a component. However, for performance reasons, Swing tables aren't implemented that way. 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 ink stamp that the table uses to stamp appropriately formatted data onto each cell. 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 dataspecifically, an Integer object. By default, the cell renderer for a Number -containing column uses a single JLabel instance to draw the appropriate numbers, right-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 editing. To choose the renderer that displays the cells in a column, a table first determines whether you specified a renderer for that particular column. (We'll tell you how to specify renderers a bit later.) If you didn't, 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 type with a list of data types for which cell renderers are registered. This list is initialized by the table, but you can add to it or change it. Currently, tables put the following types of data in the list:
Version Note: The default renderer associations for Double , Float , and Icon were added in release 1.3.
Cell editors are chosen using a similar algorithm. Remember that if you let a table create its own model, it uses Object as the type of every column. To specify more precise column types, the table model must define the getColumnClass method appropriately, as demonstrated by TableDemo.java . Keep in mind that although renderers determine how each cell or column header looks and can specify its tool tip text, renderers don't handle events. If you need to pick up the events that take place inside a table, the technique you use varies by the sort of event you're interested in.
[a] You can find TableSorter.java here: JavaTutorial/uiswing/components/example-1dot4/TableSorter.java . The next few sections tell you how to customize display and editing by specifying renderers and editors. You can specify cell renderers and editors either by column or by data type. Using a Combo Box as an Editor
Setting up a combo box as an editor is simple, as the following example shows. The bold line of code sets up the combo box as the editor for a specific column. TableColumn sportColumn = table.getColumnModel().getColumn(2); ... JComboBox comboBox = new JComboBox(); comboBox.addItem("Snowboarding"); comboBox.addItem("Rowing"); comboBox.addItem("Chasing toddlers"); comboBox.addItem("Speed reading"); comboBox.addItem("Teaching high school"); comboBox.addItem("None"); sportColumn.setCellEditor(new DefaultCellEditor(comboBox)); Figure 76 is a picture of the combo box editor in use. Figure 76. The TableRenderDemo application.
Using an Editor to Validate User-Entered Text
If a cell's default editor allows text entry, you get some error checking for free if the cell's type is specified as something other than String or Object . The error checking is a side effect of converting the entered text into an object of the proper type.
Version Note: Before 1.3, the default implementation did not in any way restrict the string that could be entered and didn't convert it from a String . You needed to put some ugly code in the model's setValueAt method to parse the entered string and prevent the cell's value from becoming a String .
The automatic checking of user-entered strings occurs when the default editor attempts to create a new instance of the class associated with the cell's column. The default editor creates this instance using a constructor that takes a String as an argument. For example, in a column whose cells have type Integer , when the user types in "123" the default editor creates the corresponding Integer using code equivalent to new Integer("123") . If the constructor throws an exception, the cell's outline turns red and refuses to let the focus move out of the cell. If you implement a class used as a column data type, you can use the default editor if your class supplies a constructor that takes a single argument of type String . If you like having a text field as the editor for a cell, but want to customize itperhaps to check user-entered text more strictly or to react differently when the text is invalidyou can change the cell editor to use a formatted text field. The formatted text field can check the value either continuously while the user is typing or after the user has indicated the end of typing (such as by pressing Enter).
table.setDefaultEditor(Integer.class, new IntegerEditor(0, 100)); The IntegerEditor class is implemented as a subclass of DefaultCellEditor [172] that uses a JFormattedTextField instead of the JTextField that DefaultCellEditor supports. It accomplishes this by first setting up a formatted text field to use an integer format and have the specified minimum and maximum values, using the API described in How to Use Formatted Text Fields (page 221). It then overrides the DefaultCellEditor implementation of the getTableCellEditorComponent , getCellEditorValue , and stopCellEditing methods, adding the operations that are necessary for formatted text fields. [172] DefaultCellEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/DefaultCellEditor.html. The override of getTableCellEditorComponent sets the formatted text field's value property (and not just the text property it inherits from JTextField ) before the editor is shown. The override of getCellEditorValue keeps the cell value as an Integer , rather than, say, the Long value that the formatted text field's parser tends to return. Finally, overriding stopCellEditing lets us check whether the text is valid, possibly stopping the editor from being dismissed. If the text isn't valid, our implementation of stopCellEditing puts up a dialog that gives the user the option of continuing to edit or reverting to the last good value. The source code is a bit too long to include here, but you can find it in IntegerEditor.java . [173] [173] IntegerEditor.java is on the CD at: JavaTutorial/uiswing/components/example-1dot4/IntegerEditor.java . Using Other Editors
Whether you're setting the editor for a single column of cells (using the TableColumn setCellEditor method) or for a specific type of data (using the JTable setDefaultEditor method), you specify the editor using an argument that adheres to the TableCellEditor interface. Fortunately, the DefaultCellEditor class implements this interface and provides constructors to let you specify an editing component that's a JTextField , JCheckBox , or JComboBox . You usually don't have to explicitly specify a check box as an editor, since columns with Boolean data automatically use a check box renderer and editor. What if you want to specify an editor that isn't a text field, check box, or combo box? Well, because DefaultCellEditor doesn't support other types of components, you must do a little more work. You need to create a class that implements the TableCellEditor [174] interface. The AbstractCellEditor [175] class is a good superclass to use. It implements TableCell-Editor 's superinterface, CellEditor , [176] saving you the trouble of implementing the event firing code necessary for cell editors. [174] TableCellEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/TableCellEditor.html. [175] AbstractCellEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/AbstractCellEditor.html. [176] CellEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/CellEditor.html. Your cell editor class needs to define at least two methods getCellEditorValue and getTableCellEditorComponent . The getCellEditorValue method, required by CellEditor , returns the cell's current value. The getTableCellEditorComponent method, required by TableCellEditor , should configure and return the component that you want to use as the editor.
Version Note: The AbstractCellEditor class was added in v1.3. Before then, implementing a cell editor for a new component type was much more difficult, since you had to implement all the CellEditor methods yourself.
Figure 77 is a picture of a table with a dialog that serves, indirectly, as a cell editor. When the user begins editing a cell in the Favorite Color column, a button (the true cell editor) appears and brings up the dialog, with which the user can choose a different color. Figure 77. The TableDialogEditDemo application with the "Pick a Color" dialog.
public class ColorEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { Color currentColor; JButton button; JColorChooser colorChooser; JDialog dialog; protected static final String EDIT = "edit"; public ColorEditor() { button = new JButton(); button.setActionCommand(EDIT); button.addActionListener(this); button.setBorderPainted(false); //Set up the dialog that the button brings up. colorChooser = new JColorChooser(); dialog = JColorChooser.createDialog( button, "Pick a Color", true, //modal colorChooser, this, //OK button handler null); //no CANCEL button handler } public void actionPerformed(ActionEvent e) { if (EDIT.equals(e.getActionCommand())) { //The user has clicked the cell, so //bring up the dialog. button.setBackground(currentColor); colorChooser.setColor(currentColor); dialog.setVisible(true); fireEditingStopped(); //Make the renderer reappear. } else { //User pressed dialog's "OK" button. currentColor = colorChooser.getColor(); } } //Implement the one CellEditor method that AbstractCellEditor doesn't. public Object getCellEditorValue() { return currentColor; } //Implement the one method defined by TableCellEditor. public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { currentColor = (Color)value; return button; } } As you can see, the code is pretty simple. The only part that's a bit tricky is the call to fireEditingStopped at the end of the editor button's action handler. Without this call, the editor would remain active, even though the modal dialog is no longer visible. The call to fireEditingStopped lets the table know that it can deactivate the editor, letting the cell be handled by the renderer again. Using Custom Renderers
This section tells you how to create and specify a cell renderer. You can set a type-specific cell renderer using the JTable method setDefaultRenderer . To specify that cells in a particular column should use a renderer, use the TableColumn method setCellRenderer . You can even specify a cell-specific renderer by creating a JTable subclass, as we'll show later. It's easy to customize the text or image rendered by the default renderer, DefaultTableCellRenderer . You just create a subclass and implement the setValue method so that it invokes setText or setIcon with the appropriate string or image. For example, here is how the default date renderer is implemented: static class DateRenderer extends DefaultTableCellRenderer { DateFormat formatter; public DateRenderer() { super(); } public void setValue(Object value) { if (formatter==null) { formatter = DateFormat.getDateInstance(); } setText((value == null) ? "" : formatter.format(value)); } } If extending DefaultTableCellRenderer doesn't do the trick, you can build a renderer using another superclass. The easiest way is to create a subclass of an existing component, making your subclass implement the TableCellRenderer [178] interface. TableCellRenderer requires just one method: getTableCellRendererComponent . Your implementation of this method should set up the rendering component to reflect the passed-in state, and then return the component. [178] TableCellRenderer API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/TableCellRenderer.html. In Figure 77 (page 403), TableDialogEditDemo 's renderer used for Favorite Color cells is a subclass of JLabel called ColorRenderer . Here are excerpts from ColorRenderer.java that show how it's implemented: [179] [179] ColorRenderer.java is on the CD at: JavaTutorial/uiswing/components/example-1dot4/ColorRenderer.java . public class ColorRenderer extends JLabel implements TableCellRenderer { ... public ColorRenderer(boolean isBordered) { this.isBordered = isBordered; setOpaque(true); //MUST do this for background to show up. } public Component getTableCellRendererComponent( JTable table, Object color, boolean isSelected, boolean hasFocus, int row, int column) { Color newColor = (Color)color; setBackground(newColor); if (isBordered) { if (isSelected) { ... //selectedBorder is a solid border in the color //table.getSelectionBackground(). setBorder(selectedBorder); } else { ... //unselectedBorder is a solid border in the color //table.getBackground(). setBorder(unselectedBorder); } } setToolTipText(...); //Discussed in the following section return this; } } Here's the code from TableDialogEditDemo.java [180] that registers a ColorRenderer instance as the default renderer for all Color data: [180] TableDialogEditDemo.java on the CD at: JavaTutorial/uiswing/components/example-1dot4/TableDialogEditDemo.java . table.setDefaultRenderer(Color.class, new ColorRenderer(true)); The next section shows a couple of examples of using TableColumn 's setCellRenderer method, so we'll skip that for now and show you how to specify a renderer for a particular cell. To specify a cell-specific renderer, you need to define a JTable subclass that overrides the getCellRenderer method. For example, the following code makes the first cell in the first column of the table use a custom renderer: TableCellRenderer weirdRenderer = new WeirdRenderer(); table = new JTable(...) { public TableCellRenderer getCellRenderer(int row, int column) { if ((row == 0) && (column == 0)) { return weirdRenderer; } // else... return super.getCellRenderer(row, column); } }; Specifying Tool Tips for Cells
By default, the tool tip text displayed for a table cell is determined by the cell's renderer. However, sometimes it can be simpler to specify tool tip text by overriding JTable 's implementation of the getToolTipText(MouseEvent) method. This section tells you how to use both techniques. To add a tool tip to a cell using its renderer, you first need to get or create the cell renderer. Then, after making sure the rendering component is a JComponent , invoke the setToolTipText method on it.
//Set up tool tips for the sport cells. DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); renderer.setToolTipText("Click for combo box"); sportColumn.setCellRenderer(renderer); Figure 78 is a picture of the resulting tool tip: Figure 78. A tool tip for cells in the Sport column.
Although the tool tip text in the previous example is static, you can also implement tool tips whose text changes depending on the state of the cell or program. Here are a couple ways to do so:
public class ColorRenderer extends JLabel implements TableCellRenderer { ... public Component getTableCellRendererComponent( JTable table, Object color, boolean isSelected, boolean hasFocus, int row, int column) { Color newColor = (Color)color; ... setToolTipText("RGB value: " + newColor.getRed() + ", " + newColor.getGreen() + ", " + newColor.getBlue()); return this; } } Figure 79 is an example of what the tool tip looks like. Figure 79. A tool tip showing the RGB value of the selected color.
Figure 80. An example of a customized tool tip for the Vegetarian column.
Here's the code from TableToolTipsDemo.java that implements tool tips for cells in the Sport and Vegetarian columns: JTable table = new JTable(new MyTableModel()) { //Implement table cell tool tips. public String getToolTipText(MouseEvent e) { String tip = null; java.awt.Point p = e.getPoint(); int rowIndex = rowAtPoint(p); int colIndex = columnAtPoint(p); int realColumnIndex = convertColumnIndexToModel(colIndex); if (realColumnIndex == 2) { //Sport column tip = "This person's favorite sport to " + "participate in is: " + getValueAt(rowIndex, colIndex); } else if (realColumnIndex == 4) { //Veggie column TableModel model = getModel(); String firstName = (String)model.getValueAt(rowIndex,0); String lastName = (String)model.getValueAt(rowIndex,1); Boolean veggie = (Boolean)model.getValueAt(rowIndex,4); if (Boolean.TRUE.equals(veggie)) { tip = firstName + " " + lastName + " is a vegetarian"; } else { tip = firstName + " " + lastName + " is not a vegetarian"; } } else { //another column //You can omit this part if you know you don't //have any renderers that supply their own tool //tips. tip = super.getToolTipText(e); } return tip; } ... } The code is fairly straightforward, except perhaps for the call to convertColumnIndexToModel , which is necessary because if the user moves the columns around, the view's index for the column doesn't match the model's index for it. For example, the user might drag the Vegetarian column (which the model considers to be at index 4) so that it's displayed as the first columnat view index 0. Since prepareRenderer gives us the view index, we need to translate the view index to a model index to be sure that we're dealing with the intended column. Specifying Tool Tips for Column Headers
You can add a tool tip to a column header by setting the tool tip text for the table's JTableHeader . Often, different column headers require different tool tip text. You can change the text by overriding the table header's getToolTipText(MouseEvent) method.
table.getTableHeader().setToolTipText( "Click to sort; Shift-Click to sort in reverse order");
Figure 81. The column header tool tip for the # of Years column.
The following code implements the tool tips. Basically, it creates a subclass of JTableHeader that overrides the getToolTipText(MouseEvent) method so that it returns the text for the current column. To associate the revised table header with the table, the JTable method createDefaultTableHeader is overridden so that it returns an instance of the JTableHeader subclass. protected String[] columnToolTips = { null, null, "The person's favorite sport to participate in", "The number of years the person has played the sport", "If checked, the person eats no meat"}; ... JTable table = new JTable(new MyTableModel()) { ... //Implement table header tool tips. protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel) { public String getToolTipText(MouseEvent e) { String tip = null; java.awt.Point p = e.getPoint(); int index = columnModel.getColumnIndexAtX(p.x); int realIndex = columnModel.getColumn(index).getModelIndex(); return columnToolTips[realIndex]; } }; } };
Version Note: Before 1.3, each column had its own header renderer, and you could use the value returned by the TableColumn method getHeaderRenderer to set a tool tip for a specific column header. For performance reasons, the default behavior is now to use a single renderer for all column headers, and getHeaderRenderer returns null . The default header renderer used for all columns is returned by the TableHeader method getDefaultRenderer , which was added in 1.3.
Sorting and Otherwise Manipulating Data
One way to perform data manipulation such as sorting is to use one or more specialized table models ( data manipulators ), in addition to the table model that provides the data (the data model). The data manipulators should sit between the table and the data model, as Figure 82 shows. Figure 82. Data manipulators sit between the table and the data model.
If you decide to implement a data manipulator , take a look at TableMap.java and TableSorter.java . [186] The TableMap class implements TableModel and serves as a superclass for data manipulators. TableSorter is a TableMap subclass that sorts the data provided by another table model. You can either change these classes, using them as a basis for writing your own data manipulator, or use the classes as is to provide sorting functionality. [186] TableMap.java and TableSorter.java are on the CD at: JavaTutorial/uiswing/components/example-1dot4/TableSorterDemo.java . To implement sorting with TableSorter , you need just three lines of code. The following listing shows the differences between TableDemo and its sorting cousin, TableSorterDemo . [187] [187] TableMap.java and TableSorterDemo.java are on the CD at: JavaTutorial/uiswing/components/example-1dot4/index.html#TableSorterDemo . TableSorter sorter = new TableSorter(new MyTableModel()); //ADDED THIS //JTable table = new JTable(new MyTableModel()); //OLD JTable table = new JTable(sorter); //NEW sorter.addMouseListenerToHeaderInTable(table); //ADDED THIS The addMouseListenerToHeaderInTable method adds a mouse listener that detects clicks over the column headers. When the listener detects a click, it sorts the rows based on the clicked column. As Figure 83 shows, when you click "Last Name," the rows are reordered so that "Campione" is in the first row and "Zakhour" is in the last. When you Shift-click a column header, the rows are sorted in reverse order. Figure 83. The TableSorterDemo application with the Last Name column sorted alphabetically .
The Table API
Tables 88 through 92 cover just part of the table API. For more information about the table API, see the API documentation for JTable [188] and for the various classes and interfaces in the table package. [189] Also see The JComponent Class (page 53), which describes the API that JTable inherits from JComponent . [188] JTable API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTable.html. [189] Table package API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/package-summary.html. Table 88. Table-Related Classes and Interfaces
Table 89. Creating and Setting up a Table
Table 90. Manipulating Columns
Table 91. Using Editors and Renderers
Table 92. Implementing Selection
Examples That Use Tables
This table lists examples that use JTable and where those examples are described.
|
< Day Day Up > |