Bom, aqui vai um TableModel bem comentado e em anexo está um exemplo de como usá-lo (só não postei tudo aqui por causa do tamanho das classes). Creio que esses exemplos possam ajudar quem ainda tem dúvida quanto à TableModel. A classe:
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
/**
* A interface {@link TableModel} define 9 métodos, sendo que 2 deles cuidam da
* parte de listeners. Esses dois métodos (addTableModelListener e
* removeTableModelListener) nós delegaremos para a classe
* {@link AbstractTableModel}. Os outros 7 nós implementaremos. Uma pequena
* explicação de cada um deles (para mais detalhes, consulte a documentação):<br>
* <ul>
* <li>getColumnClass: retorna a classe de determinada coluna. Útil para os
* renderers saberem como exibir determinada informação.</li>
* <li>getColumnCount: retorna o número de colunas que nossa tabela terá
* (geralmente é o número de atributos do objeto que será exibido).</li>
* <li>getColumnName: retorna o nome da coluna. Será usado no cabeçalho da
* tabela.</li>
* <li>getRowCount: retorna o número de registros. Geralmente é a quantidade de
* elementos que há em nossa lista.</li>
* <li>getValueAt: retorna o valor que está contido em determinada célula. Para
* resgatarmos esse valor, primeiro pegamos o objeto da linha e depois, a partir
* do número da coluna, retornamos uma de suas propriedades.</li>
* <li>isCellEditable: informa se determinada célula pode ser editada ou não.</li>
* <li>setValueAt: é chamado quanto o valor de determinada célula é alterada.
* Devemos modificar nosso objeto para refletir essa mudança.</li>
* </ul>
*/
public class ClienteTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
/**
* Essa lista armazenará os objetos do tipo {@link Cliente} atualmente
* exibidos na tablela.
*/
private List<Cliente> clientes;
// ==============================================================
// Construtores.
// ==============================================================
/**
* Constutor que simplesmente instancia o nosso {@link List} que usaremos
* para guardar os valores.
*/
public ClienteTableModel() {
// no construtor, instanciamos o List
clientes = new ArrayList<Cliente>();
}
/**
* Criamos um construtor de conveniência para já popular a lista.
*
* @param lista
* a lista de clientes a ser adicionada.
*/
public ClienteTableModel(List<Cliente> lista) {
this();
clientes.addAll(lista);
}
// ==============================================================
// Métodos implementados.
// ==============================================================
@Override
public Class<?> getColumnClass(int coluna) {
// todas as colunas representam uma String
return String.class;
}
@Override
public int getColumnCount() {
// esse método deve retornar o número de colunas. No caso, 3 (uma para o
// nome, uma para o sobrenome e uma para o apelido).
return 3;
}
@Override
public String getColumnName(int coluna) {
// vamos retornar o nome de cada coluna
switch (coluna) {
case 0:
return "Nome"; // o nome da primeira coluna
case 1:
return "Sobrenome"; // o nome da segunda
case 2:
return "Apelido"; // e o da terceira
default:
return ""; // isso nunca deve ocorrer, pois temos só 3 colunas
}
}
@Override
public int getRowCount() {
// retorna o número de linhas, ou seja, a quantidade de entradas na
// nossa lista.
return clientes.size();
}
@Override
public Object getValueAt(int linha, int coluna) {
// vai retornar o valor de determinada célula. A linha representa a
// posição do Cliente na nossa lista e a coluna vai ser: 1 - nome, 2 -
// sobrenome e 3 - apelido
// primeiro vamos pegar o Cliente da linha
Cliente c = clientes.get(linha);
// façamos um switch
switch (coluna) {
case 0:
return c.getNome(); // retornamos o nome
case 1:
return c.getSobrenome(); // retornamos o sobrenome
case 2:
return c.getApelido(); // e o apelido
default:
return null; // isso nunca deve ocorrer, pois temos só 3 colunas
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
// nesse caso, todas as células são editáveis
return true;
}
@Override
public void setValueAt(Object valor, int linha, int coluna) {
// aqui devemos atualizar o valor de nossos Clientes
// vemos em qual linha ele está
Cliente c = clientes.get(linha);
// e vemos o que será atualizado
switch (coluna) {
case 0:
c.setNome(valor.toString()); // mudamos o nome
break;
case 1:
c.setSobrenome(valor.toString()); // mudamos o sobrenome
break;
case 2:
c.setApelido(valor.toString()); // e o apelido
break;
}
// é importante notificar os listeners a cada alteração
fireTableDataChanged();
}
// ==============================================================
// Até aqui seria o mínimo necessário para um TableModel funcional, mas
// ainda não há métodos para adicionar, remover ou resgatar objetos. Vamos
// criá-los.
// ==============================================================
/**
* Adiciona um novo {@link Cliente} e notifica os listeners que um novo
* registro foi adicionado.
*/
public void adiciona(Cliente c) {
clientes.add(c);
// informamos os listeners que a linha (size - 1) foi adicionada
fireTableRowsInserted(clientes.size() - 1, clientes.size() - 1);
}
/**
* Similar ao {@link #adiciona(Cliente)}, porém para remover. Recebe o
* índice do cliente a ser removido. Se não souber o índice, use o método
* {@link #getIndice(Cliente)} antes.
*/
public void remove(int indice) {
clientes.remove(indice);
fireTableRowsDeleted(indice, indice);
}
/**
* Retorna o índice de determinado cliente.
*/
public int getIndice(Cliente c) {
return clientes.indexOf(c);
}
/**
* Adiciona todos os clientes na lista à este modelo.
*/
public void adicionaLista(List<Cliente> lista) {
int i = clientes.size();
clientes.addAll(lista);
fireTableRowsInserted(i, i + lista.size());
}
/**
* Esvazia a lista.
*/
public void limpaLista() {
int i = clientes.size();
clientes.clear();
fireTableRowsDeleted(0, i - 1);
}
}
Se tirarmos os comentários, a classe terá menos de 110 linhas (nada muito absurdo). Agora vamos discutir as capacidades dessa classe. Para exemplificar, vamos tratar de 2 situações usando o exemplo em anexo:
- A tabela deve estar ordenada.
Fazer isso com DefaultTableModel seria simplesmente inviável. Com nosso TableModel, tudo que temos que fazer é criar um método para ordenar a lista na nossa classe que extende AbstractTableModel:
public void ordenaLista() {
Collections.sort(clientes);
}
E, no método tableChanged do nosso TableModelListener, chamaríamos o método ordenaLista():
public void tableChanged(TableModelEvent e) {
modelo.ordenaLista();
// continua....
}
Pronto! Temos uma lista ordenada.
- Não queremos que o sobrenome seja editável. Basta mudarmos o método isCellEditable(int, int) para que, ao invés de retornar sempre true, retorne true somente se a coluna for diferente de 1:
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex != 1;
}
Percebe como é simples fazer coisas que seriam muito complexas? Podemos também trabalhar com Map ou arrays no nosso Model. Vai depender da necessidade. E depois que você entende como criar seu Model percebe que é muito mais interessante do que fazer aquele monte de casts ou gambiarras no meio do código. Espero que ajude.