Java Swing Tutorial - Java Swing JTable








A JTable displays data using rows and columns.

A JTable does not store data. It only displays data.

JTable uses a model to store the data, the number of columns, and the number of rows.

An instance of the TableModel interface represents the model for a JTable.

The DefaultTableModel class is an implementation of the TableModel interface.

When we use the default constructor of the JTable class, Java sets an instance of the DefaultTableModel class as its model.

If we want to add or remove columns/rows, we must work with its model. we can get the reference of the model of a JTable using its getModel() method.

Let's add two rows and three columns to the table.

DefaultTableModel tableModel  = (DefaultTableModel)table.getModel();

// Set  the   number of  rows  to 2 
tableModel.setRowCount(2);

// Set  the   number of  columns  to 3 
tableModel.setColumnCount(3);

To set the value for a cell in the table, use the setValueAt(Object data, int row, int column) method of the table's model or the table to set a value in its cell.

The following code sets the value at (0, 0) in the table's model.

tableModel.setValueAt("new value",  0, 0);

Set the value at (0, 0) in the table. Works the same as setting the value using the table's model

table.setValueAt("new value", 0, 0);

To support scroll we need to we add the table to a JScrollPane.

The labels for the column headers are set as A, B, and C and we can double-click any cell to start editing the value in the cell.

To get the value in a cell, use the getValueAt(int row, int column) method of the table's model or the JTable.

We can add more columns or rows to the JTable by using the addColumn() and addRow() methods of the DefaultTableModel class.

We can use the removeRow(int row) method of the its model class to remove a row from the JTable.

To set custom labels for column headers using the model's setColumnIdentifiers() method as follows:

// Store the   column  headers in an  array
Object[] columnHeaderLabels = new Object[]{"Name", "DOB", "Gender"};

// Set  the   column  headers for the   table using its  model tableModel.setColumnIdentifiers(columnHeaderLabels);

We can create a JTable with two rows and three columns as shown:

JTable table  = new JTable(2, 3);

Two other constructors for the JTable allows us to set the number of rows and columns, and data.

JTable(Object[][] rowData,   Object[] columnNames)
JTable(Vector rowData,   Vector   columnNames)

The following code shows how to use the above constructors.

Object[] columnNames = {"ID", "Name",  "Gender"  }  ;

Object[][] rowData  = new Object[][]  {
    {new Integer(1), "Tom", "Male"  },
    {new Integer(2), "Jane", "Female"}
};

// Create a  JTable with  the   data and  the   column  headers
JTable table  = new JTable(rowData,  columnNames);




TableModel

The TableModel interface defines the model for a JTable. Here is the declaration of the TableModel interface:

public interface  TableModel{
    public int  getRowCount();
    public int  getColumnCount();
    public String getColumnName(int  columnIndex);
    public Class<?>   getColumnClass(int  columnIndex);
    public  boolean isCellEditable(int rowIndex, int  columnIndex);
    public Object getValueAt(int rowIndex, int  columnIndex);
    public void  setValueAt(Object aValue, int  rowIndex, int  columnIndex);
    public void  addTableModelListener(TableModelListener l);
    public void  removeTableModelListener(TableModelListener l);
}

The AbstractTableModel class implements the TableModel interface. It provides an empty implementation for the methods of the TableModel interface.

We have to implement at least the following three methods in custom table model class to get a read-only table model:

public int  getRowCount();
public int  getColumnCount();
public Object getValueAt(int row,  int  column);

The DefaultTableModel class inherits from the AbstractTableModel class. It provides a default implementation for all methods in the TableModel interface.

It uses a Vector of Vectors to store the table's data.

The following code implements a simple table model using an array of arrays to store data.

import java.awt.BorderLayout;
import java.awt.Container;
/*from  w  ww  . j  a  v  a2s .c  o  m*/
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

class SimpleTableModel extends AbstractTableModel {
  private Object[][] data = {};
  private String[] columnNames = { "ID", "Name", "Gender" };
  private Class[] columnClass = { Integer.class, String.class, String.class };
  private Object[][] rowData = new Object[][] {
      { new Integer(1), "Tom", "Male" },
      { new Integer(2), "Jack", "Female" } };

  public SimpleTableModel() {
  }

  @Override
  public int getRowCount() {
    return rowData.length;
  }

  @Override
  public int getColumnCount() {
    return columnNames.length;
  }

  @Override
  public String getColumnName(int columnIndex) {
    return columnNames[columnIndex];
  }

  @Override
  public Class getColumnClass(int columnIndex) {
    return columnClass[columnIndex];
  }

  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {
    boolean isEditable = true;
    if (columnIndex == 0) {
      isEditable = false; // Make the ID column non-editable
    }
    return isEditable;
  }

  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {
    return rowData[rowIndex][columnIndex];
  }

  @Override
  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    rowData[rowIndex][columnIndex] = aValue;
  }
}

public class Main extends JFrame {
  public Main() {
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);

    JTable table  = new JTable(new SimpleTableModel());

    Container contentPane = this.getContentPane();
    contentPane.add(new JScrollPane(table), BorderLayout.CENTER);
  }

  public static void main(String[] args) {
    Main bf = new Main();
    bf.pack();
    bf.setVisible(true);
  }
}




Sort

To add data sorting capability, call setAutoCreateRowSorter(true).

We can sort data in a column by clicking the column's header. After we call this method, a JTable will display an up/down arrow as part of a column header to indicate that a column is sorted in ascending or descending order.

We can use a row filter that will hide rows in a JTable based on some criteria.

import java.awt.BorderLayout;
import java.awt.Container;
/*from ww  w . j  av a2 s . com*/
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;

class SimpleTableModel extends AbstractTableModel {
  private Object[][] data = {};
  private String[] columnNames = { "ID", "Name", "Gender" };
  private Class[] columnClass = { Integer.class, String.class, String.class };
  private Object[][] rowData = new Object[][] {
      { new Integer(1), "Tom", "Male" }, { new Integer(2), "Jack", "Female" } };

  public SimpleTableModel() {
  }

  @Override
  public int getRowCount() {
    return rowData.length;
  }

  @Override
  public int getColumnCount() {
    return columnNames.length;
  }

  @Override
  public String getColumnName(int columnIndex) {
    return columnNames[columnIndex];
  }

  @Override
  public Class getColumnClass(int columnIndex) {
    return columnClass[columnIndex];
  }

  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {
    boolean isEditable = true;
    if (columnIndex == 0) {
      isEditable = false; // Make the ID column non-editable
    }
    return isEditable;
  }

  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {
    return rowData[rowIndex][columnIndex];
  }

  @Override
  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    rowData[rowIndex][columnIndex] = aValue;
  }
}

public class Main extends JFrame {
  public Main() {
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);

    JTable table = new JTable(new SimpleTableModel());

    TableRowSorter sorter = new TableRowSorter(table.getModel());
    table.setRowSorter(sorter);

    RowFilter<SimpleTableModel, Integer> IDFilter = new RowFilter<SimpleTableModel, Integer>() {
      @Override
      public boolean include(
          Entry<? extends SimpleTableModel, ? extends Integer> entry) {
        SimpleTableModel model = entry.getModel();
        int rowIndex = entry.getIdentifier().intValue();
        Integer ID = (Integer) model.getValueAt(rowIndex, 0);
        if (ID.intValue() <= 100) {
          return false; // Do not show rows with an ID <= 100
        }
        return true;
      }
    };

    sorter.setRowFilter(IDFilter);

    Container contentPane = this.getContentPane();
    contentPane.add(new JScrollPane(table), BorderLayout.CENTER);
  }

  public static void main(String[] args) {
    Main bf = new Main();
    bf.pack();
    bf.setVisible(true);
  }
}

Filter

The RowFilter is an abstract class and we must override its include() method to specify filter criteria.

RowFilter has several static methods that return RowFilter objects that we can use directly with a RowSorter object.

The following code shows how to create row filters:

To create a filter that will show only rows that starts with "Tom" in the second column (column index = 1)

RowFilter nameFilter  = RowFilter.regexFilter("^Tom*", 1);

To create a filter that will show only rows that has a "Female" value in its third column (column index = 2)

RowFilter genderFilter  = RowFilter.regexFilter("^Female$", 2);

To create a filter that will show only rows that has 3rd, 5th and 7th columns values starting with "A"

RowFilter anyFilter1  = RowFilter.regexFilter("^A*", 3, 5, 7);

To create a filter that will show only rows that has any column whose value starts with "A"

RowFilter anyFilter2  = RowFilter.regexFilter("^A*");