Introducing
To Create a Tablemodel
, it is necessary to extend the class AbstractTableModel
, which is an abstract class with the main functionalities necessary to work with the table completion. We can also implement the interface TableModel
, but the first option already implements it, adding other features that makes it more complete for the case.
Main methods of the Abstracttablemodel class
Before creating a TableModel
, it is important to understand the workings of some of the methods we will use:
isCellEditable(int rowIndex, int columnIndex)
= receives the cell coordinates(row and column) and returns a boolean that will indicate whether the cell content is editable or not;
getRowCount()
= returns the total of rows the table has;
getColumnCount()
= returns the total of columns the table has;
getColumnName(int column)
= receives an index and returns the name of the column of that given input;
getColumnClass(int columnIndex)
= receives a column index and returns the data type of the cells in that column;
getValueAt(int rowIndex, int columnIndex)
= this method is that will fill the table, cell by cell, according to its coordinates(row and column).
setValueAt(Object aValue, int rowIndex, int columnIndex)
= this method will be called whenever a cell is changed in the table.
fireTableDataChanged()
= shall notify all listeners
that there has been a change in the table and that it should be redrawn.
With these methods, we can already create a basis of TableModel
so that the table knows how to fill in with objects of the type Funcionario
.
Creating the Tablemodel
We will create the class FuncionarioTableModel
, you will receive a list of objects:
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class FuncionarioTableModel extends AbstractTableModel {
//aqui transformei em coluna cada propriedade de Funcionario
//que eu quero que seja exibida na tabela
private String colunas[] = {"nome", "idade", "matricula", "admitido"};
private ArrayList<Funcionario> funcionarios;
private final int COLUNA_NOME = 0;
private final int COLUNA_IDADE = 1;
private final int COLUNA_MATRICULA = 2;
private final int COLUNA_ADMITIDO = 3;
public FuncionarioTableModel(ArrayList<Funcionario> funcionarios) {
this.funcionarios = funcionarios;
}
//retorna se a célula é editável ou não
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
//retorna o total de itens(que virarão linhas) da nossa lista
@Override
public int getRowCount() {
return funcionarios.size();
}
//retorna o total de colunas da tabela
@Override
public int getColumnCount() {
return colunas.length;
}
//retorna o nome da coluna de acordo com seu indice
@Override
public String getColumnName(int indice) {
return colunas[indice];
}
//determina o tipo de dado da coluna conforme seu indice
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case COLUNA_NOME:
return String.class;
case COLUNA_IDADE:
return Integer.class;
case COLUNA_MATRICULA:
return Integer.class;
case COLUNA_ADMITIDO:
return Boolean.class;
default:
return String.class;
}
}
//preenche cada célula da tabela
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Funcionario funcionario = this.funcionarios.get(rowIndex);
switch (columnIndex) {
case COLUNA_NOME:
return funcionario.getNome();
case COLUNA_IDADE:
return funcionario.getIdade();
case COLUNA_MATRICULA:
return funcionario.getMatricula();
case COLUNA_ADMITIDO:
return funcionario.isAdmitido();
}
return null;
}
//altera o valor do objeto de acordo com a célula editada
//e notifica a alteração da tabela, para que ela seja atualizada na tela
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
//o argumento recebido pelo método é do tipo Object
//mas como nossa tabela é de funcionários, é seguro(e até recomendável) fazer o cast de suas propriedades
Funcionario funcionario = this.funcionarios.get(rowIndex);
//de acordo com a coluna, ele preenche a célula com o valor
//respectivo do objeto de mesmo indice na lista
switch (columnIndex) {
case COLUNA_NOME:
funcionario.setNome(String.valueOf(aValue));
break;
case COLUNA_IDADE:
funcionario.setIdade((int) aValue);
break;
case COLUNA_MATRICULA:
funcionario.setMatricula((int) aValue);
break;
case COLUNA_ADMITIDO:
funcionario.setAdmitido((boolean) aValue);
}
//este método é que notifica a tabela que houve alteração de dados
fireTableDataChanged();
}
}
The code is commented on what each method is doing. Set constants as column index for code to be easier to understand, and columns are already defined in a list of String
, but can adapt as you like, either by passing a list of columns, as happens with the DefaultTableModel
, or set directly in the method getColumnName
, but I believe that this is unnecessary complexity for this example.
Filling Jtable with the Tablemodel
Once ready, there are two ways to pass this Tablemodel to the table: passing directly to the Jtable constructor by instantiating it or passing the model via method setModel()
. Obviously, when creating the table, the first option is the one that should be used:
import java.awt.EventQueue;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
public class JTableExample extends JFrame {
private JTable tabela;
private JScrollPane scrollPainel;
public JTableExample() {
renderizarTela();
}
private void renderizarTela() {
//4 ojetos criados para popular a tabela
Funcionario f1 = new Funcionario("Roberto", 33, 1220);
Funcionario f2 = new Funcionario("Diego", 25, 1615);
Funcionario f3 = new Funcionario("Afonso", 25, 1458);
Funcionario f4 = new Funcionario("Sergio", 42, 1165);
ArrayList<Funcionario> funcionarios = new ArrayList<>();
funcionarios.add(f1);
funcionarios.add(f2);
funcionarios.add(f3);
funcionarios.add(f4);
//cria um objeto do nosso model
FuncionarioTableModel model = new FuncionarioTableModel(funcionarios);
//instancia a tabela já com o model como argumento
this.tabela = new JTable(model);
this.scrollPainel = new JScrollPane(tabela);
this.add(scrollPainel);
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JTableExample tb = new JTableExample();
tb.setLocationRelativeTo(null);
tb.setVisible(true);
}
});
}
}
And the result:
The most interesting thing is that, because we have defined the type in each column, it is not necessary to worry about problems of cast numerical. For example, pass letters as age or matricula, the table already restricts itself to this and does not accept a data that is not of that type. Of course, if you pass a number as a name, it will not be stopped since it will be understood as String
.
Completion
The class AbstractTableModel
has other methods to make the table much more powerful, and still can add other features like deletion and addition of lines, do operations with database, but will not be deepened by didactic issues.
From the above, one can see that it is not so complicated to implement a TableModel
, not to mention that it facilitates the maintainability of the code and makes our table much more flexible than using DefaultTableModel
.
Of course this is applicable to cases with less complexity, or for academic purposes, since there are components that facilitate and even automate this creation (such as examples cited by Anthony Accioly), but it is important to understand its functioning to know how to handle this component.
Only for educational purposes, while implementing a
TableModel
in nail is certainly an interesting exercise, in practice in 99% of cases are met by generic Frameworks such as Jgoodies Binding, Beans Binding and Glazed Lists with your own templates. In the Javafx worldTableView
,TableColumn
andObservableList
also replaced the model-based architecture. In practice it is rare to need to implement aTableModel
.– Anthony Accioly
Very good... Great code and explanations. Used in Netbeans. .Bingo... Perfect I made several BEANS and DAOS.. Quiet.. Success.... Luciano Deluqui
– user74298