JTable

Whereas the JList class displayed a list of items in a single column, the JTable class is capable of showing items that each have several columns. You need to attach a TableModel object (which references the item data) to the JTable. The most common way of doing this is to subclass the AbstractTableModel class and override three methods that provide the total number of rows, the total number of columns, and the object at a particular row/column intersection, or "cell".

Continuing the simple examples from earlier, where you are using the strings "Red", "Green" and "Blue", assume you want a three-column table where the first column will be the normal text of the colour, the second column the text in upper-case, and the third column the number of characters in the word. Define the following inner class inside DemoUI, and note that you need to import package java.swing.table

private class ColourTableModel extends AbstractTableModel {
        
    private ArrayList<String> colours;
        
    public ColourTableModel() {
        colours = new ArrayList<String>();
        colours.add("Red");
        colours.add("Green");
        colours.add("Blue");
    }

    @Override
    public int getRowCount() {
        return colours.size();
    }

    @Override
    public int getColumnCount() {
        return 3;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        // Get the name of the colour at the required row index
        String colourName = colours.get(rowIndex);
            
        // Which column is required?
        switch (columnIndex) {
            case 0:
                // First column - return name of colour
                return colourName;
                    
            case 1:
                // Second column - return name in upper case
                return colourName.toUpperCase();
                    
            case 2:
                // Third column - return number of characters in name
                return colourName.length();
                    
            default:
                    throw new IllegalStateException("columnIndex not valid" + columnIndex);
        }
    }
        
}
 

The class ColourTableModel extends the Java supplied AbstractTableModel

The constructor is identical to that used for ColourListModel – it adds the three strings to an ArrayList object

The getRowCount() method returns the total number of rows in the table, which will correspond to the total number of items in the ArrayList object

The getColumnCount() method returns the total number of columns in the table, which in this case is three

The getValueAt() method provides two arguments, being the row index and the column index, where each of them starts from zero. You can think of a table as being like a spreadsheet, where the row and column indexes together point to a particular cell. The method does the following:

  • Retrieves the text from the ArrayList object using the specified row index
  • If the column index is zero then the first column in wanted, so just return the text as-is
  • If the column index is one then the second column in wanted, so return the text converted to upper-case
  • If the column index is two then the third column in wanted, so return the number of characters in the text
  • Because a switch statement was used to determine the column index the compiler will check that something is being returned in all cases, including those where columnIndex is not 0, 1 or 2. Even though this should never happen (because the getColumnCount() method returns 3) the compiler does not know this so you need to specify a default processing for all other cases. In this case you simply throw an unchecked exception

Now in the buildUI() method of the main body of DemoUI you can instantiate a ColourTableModel object, assign it to a new JTable object, put the table in a scroll pane and place it on the panel:

TableModel model = new ColourTableModel();
JTable table = new JTable(model);
panel.add(new JScrollPane(table));
 

The JTable object takes care of invoking the appropriate ColourTableModel methods for you automatically. You should see the following table displayed:

JTable

There are a couple of points to note about the table as it stands:

  1. Because you have not specifically set the column headings it has defaulted to spreadsheet-style names of A, B, C etc.
  2. All items of data are left-aligned. While this is a sensible default for strings, numbers generally look better when right aligned

To set the column headers you can override the getColumnName() method inside the ColourTableModel inner class:

@Override
public String getColumnName(int columnIndex) {
    String[] columnHeaders = {"Colour",
                              "Colour in Uppercase",
                              "Number of Characters"};
    return columnHeaders[columnIndex];
}
 

To make numbers right-aligned you can take advantage of the fact that tables are capable of using default alignments based upon the class of the Object which is returned by getValueAt(). To do this, override the getColumnClass() method inside ColourTableModel:

@Override
public Class getColumnClass(int columnIndex) {
    return getValueAt(0, columnIndex).getClass();
}
 
  • The getValueAt() method is called using the specified column index on row zero to determine the object which appears there. The getClass() method is then obtained from that object and returned. The table now has the information it needs to render the alignment sensibly

The table should now look as follows:

JTable with named columns and correct data alignment

If you would like users to be able to sort the data on any of the columns, you can add the following statement after you instantiate the JTable object:

table.setAutoCreateRowSorter(true); 

The user can now click on any column header to sort the table by that column. Each time a column is clicked it toggles between being sorted in ascending and descending sequence. A small arrow appears in the column heading to indicate the sequencing.

It's possible to enable table cells to be directly edited by the user. Suppose, for example, you want to enable the text of the colour to be edited in the first column. Because the values displayed in columns two and three depend on the text in column one then only column one should be editable. First, you need to override the isCellEditable() method inside ColourTableModel:

@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
    return (columnIndex == 0);
}
 
  • The method needs to return true if the cell should be editable and false otherwise. Above, true will be returned whenever the column index is zero

If you run the application, you can now double-click inside any entry in the first column to make the cell go into edit mode:

Editable JTable

Try changing the text to something else and press Enter on your keyboard. You will notice the text revert to what it was previously! To save the edited change you need to override the setValue() method as well:

@Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
    colours.remove(rowIndex);
    colours.add(rowIndex, (String) value);
}
 
  • Because your model data is being stored in the ArrayList object called colours, the method firstly removes the old value there at the modified row index and then adds the new value at the same position.

If you run the application again your change is made but you will notice that the upper-case version and number of characters in the second and third columns have not been updated. You can fix this by adding one more statement to the above method to tell the table that the data has been modified so that it can perform a refresh:

@Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
    colours.remove(rowIndex);
    colours.add(rowIndex, (String) value);
    fireTableDataChanged();
}