Quando precisamos utilizar JTables, somos obrigado a fornecer um modelo de dados para ela. Isso se dá usando a classe DefaultTableModel ou criando uma subclasse de AbstractTableModel.
O problemas é que muitas vezes, queremos apenas mostrar alguns dados e poder alterá-los.
Utilizando DefaultTableModel temos que fornecer um vetor de dados e outro de colunas. Com os dados no vetor, a manipulação fica meio complicado por diversos fatores como aumentar sua capacidade.
Outra possibilidade é fornecer um Vector com os dados e outro com as colunas. O Vector é uma classe sincronizada, o que significa perda de performance sendo que muitas vezes não precisaremos de uma estrutura de dados sincronizada.
Criando nossa própria subclasse de AbstractTableModel pode ser um pouco trabalhoso pois poderemos ter diversos TableModels para apenas mostrar dados.
A solução que encontrei foi implementar a classe AbstractTableModel para que receba uma List generica e através de Reflection, acesse os campos tanto para mostrar nas células quanto para atualização.
Para criar a classe de Reflection utilizei o pacote org.apache.commons.beanutils que pode ser baixado em http://commons.apache.org/beanutils/download_beanutils.cgi
Utilizando a classe PropertyUtils, temos os métodos setSimpleValue(target, fieldName, data) que atualiza uma propriedade e o método getSimpleValue(target, fieldName), para acessar uma propriedade, desde que a mesma esteja encapsulada (get() set()).
Então, a classe TableModelDinamic ficará assim:
import java.util.List;
import javax.swing.table.AbstractTableModel;
import orquestralibrary.util.Reflection;
public class TableModelDinamic extends AbstractTableModel {
protected List list;
protected String[] columnNames;
public TableModelDinamic() {
super();
}
public TableModelDinamic(List list) {
setList(list);
}
public TableModelDinamic(List list, String [] cols)
{
setList(list);
columnNames = cols;
}
public int getRowCount() {
return list.size();
}
public int getColumnCount() {
if(columnNames == null)
return list.get(0).getClass()
.getDeclaredFields().length;
return columnNames.length;
}
public Object getValueAt(int row, int column) {
return Reflection.getFieldValue(list.get(row), getFieldName(column));
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
Reflection.setFieldValue(list.get(rowIndex), getFieldName(columnIndex), aValue);
}
private String getFieldName(int idx)
{
if(columnNames == null)
return list.get(0).getClass()
.getDeclaredFields()[idx].getName();
return columnNames[idx];
}
@Override
public String getColumnName(int column) {
return getFieldName(column);
}
public List getList() {
return list;
}
public void setList(List _list) {
list = _list;
fireTableDataChanged();
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
}
O construtor do nosso model recebe uma lista como argumento no construtor ou uma lista mais os nomes das colunas que serão mostradas na JTable.
Se voce quiser trocar a lista, basta usar o método setList do model.
Para utilizar esta classe, basta apenas instanciar o model passando a sua lista.
import java.util.ArrayList;
import java.util.List;
/**
*
* @author marciliosouza
*/
public class Frame extends javax.swing.JFrame {
/** Creates new form Frame */
public Frame() {
super("Simple TableModel");
initComponents();
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jButton1 = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
jButton2 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButton1.setText("Proximo");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jTable1.setModel(getModel());
jScrollPane1.setViewportView(jTable1);
jButton2.setText("Imprimir");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 375, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addComponent(jButton2)
.addGap(77, 77, 77)
.addComponent(jButton1)))
.addGap(21, 21, 21))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton2))
.addContainerGap())
);
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
((TableModelDinamic)jTable1.getModel()).setList(getNextList());
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
for(Object p : ((TableModelDinamic)jTable1.getModel())
.getList())
{
System.out.println("Nome .: "+((Pessoa)p).getNome() +" Profissao .: "+ ((Pessoa)p).getProfissao() + " Idade .: "+((Pessoa)p).getIdade());
}
System.out.println();
}
List<List<Pessoa>> lists;
int idx = 0;
public List<Pessoa> getNextList()
{
if(lists == null)
{
List<Pessoa> list1 = new ArrayList<Pessoa>();
list1.add(new Pessoa("Jose", 33, "Arquiteto"));
list1.add(new Pessoa("Maria", 29, "Medica"));
List<Pessoa> list2 = new ArrayList<Pessoa>();
list2.add(new Pessoa("Marcilio", 22, "Programdor"));
list2.add(new Pessoa("Kelly", 22, "Psicologa"));
List<Pessoa> list3 = new ArrayList<Pessoa>();
list3.add(new Pessoa("Alexandre", 24, "Programdor"));
list3.add(new Pessoa("Natalia", 22, "Empresaria"));
List<Pessoa> list4 = new ArrayList<Pessoa>();
list4.add(new Pessoa("Jalim", 24, ""));
lists = new ArrayList<List<Pessoa>>();
lists.add(list1);
lists.add(list2);
lists.add(list3);
lists.add(list4);
}
if(idx <= lists.size()-1)
return lists.get(idx++);
idx = 0;
return lists.get(idx);
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
new Frame().setVisible(true);
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable1;
// End of variables declaration
public TableModelDinamic getModel()
{
return new TableModelDinamic(getNextList());
}
}
Em alguns casos, mesmo utilizando nosso TableModelDinamic, pode ser preciso criar um model especifico. Um exemplo disso é prcisarmos informar em uma coluna uma “quantidade”, em outra o “preco” e queremos que em uma terceira coluna, seja mostrado um subtotal.
Neste caso podemos estender TableModelDinamic e fazer estes cálculos no método setValue.