É certo que o número de usuários Swing Java decaiu muito ao longo dos anos, mas para os que ainda usam a tecnologia padrão do Java para desenvolver interfaces gráficas em nível desktop, conhecer o melhor que o Swing pode fazer por você faz toda a diferença.
Especificamente os componentes gráficos de tabelas, representado em código pela classe JTable, não dispõe de um modo interativo de preencher suas telas como o encontrado em planilhas simples. Adicionando um registro simples, muitas vezes, exige-se que a interface também tenha um botão "Adicionar" ou uma caixa de diálogo pedindo para valores, por exemplo, para em seguida atualizar a JTable em si.
Neste artigo veremos um exemplo de uma tabela de listagem de pessoas contendo Nome, Endereço e Fone, sem a necessidade de um botão "Adicionar". Tudo o que precisamos é o nosso TableModel próprio definido, um TableModelListener, um TableCellRenderer personalizado e alguns métodos auxiliares.
Primeiro vamos criar a classe que conterá os valores das pessoas. Como um Value Object comum (Listagem 1).
package br.edu.devmedia.jtable;
public class RegistroPessoa {
protected String nome;
protected String endereco;
protected String fone;
public RegistroPessoa() {
nome = "";
endereco = "";
fone = "";
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public String getFone() {
return fone;
}
public void setFone(String fone) {
this.fone = fone;
}
}
Em seguida, criaremos o TableModelInterativo, uma classe que será responsável por implementar o AbstractTableModel e criar o nosso próprio modelo de tabela. Veja na Listagem 2 o código para tal.
package br.edu.devmedia.jtable;
import java.util.Vector;
import javax.swing.table.AbstractTableModel;
/**
* Classe que representa o modelo de tabela a ser seguido para desenhar a tabela
* principal
*/
public class TableModelInterativo extends AbstractTableModel {
private static final long serialVersionUID = 1L;
public static final int INDEX_NOME = 0;
public static final int INDEX_ENDERECO = 1;
public static final int INDEX_FONE = 2;
public static final int INDEX_ESCONDIDO = 3;
protected String[] nomeColunas;
protected Vector<RegistroPessoa> vetorDados;
public TableModelInterativo(String[] columnNames) {
this.nomeColunas = columnNames;
vetorDados = new Vector<RegistroPessoa>();
}
public String getNomeColuna(int coluna) {
return nomeColunas[coluna];
}
public boolean isCellEditable(int linha, int coluna) {
if (coluna == INDEX_ESCONDIDO) {
return false;
} else {
return true;
}
}
public Object getValueAt(int linha, int coluna) {
RegistroPessoa registroPessoa = (RegistroPessoa) vetorDados.get(linha);
switch (coluna) {
case INDEX_NOME:
return registroPessoa.getNome();
case INDEX_ENDERECO:
return registroPessoa.getEndereco();
case INDEX_FONE:
return registroPessoa.getFone();
default:
return new Object();
}
}
public void setValorEm(Object valor, int linha, int coluna) {
RegistroPessoa record = (RegistroPessoa) vetorDados.get(linha);
switch (coluna) {
case INDEX_NOME:
record.setNome((String) valor);
break;
case INDEX_ENDERECO:
record.setFone((String) valor);
break;
case INDEX_FONE:
record.setFone((String) valor);
break;
default:
System.out.println("invalid index");
}
fireTableCellUpdated(linha, coluna);
}
public int getRowCount() {
return vetorDados.size();
}
public int getColumnCount() {
return nomeColunas.length;
}
public boolean hasLinhasVazias() {
if (vetorDados.size() == 0) {
return false;
}
RegistroPessoa registroPessoa =
(RegistroPessoa) vetorDados.get(vetorDados.size() - 1);
if (registroPessoa.getNome().trim().equals("")
&& registroPessoa.getEndereco().trim().equals("")
&& registroPessoa.getFone().trim().equals("")) {
return true;
} else {
return false;
}
}
public void addLinhaVazia() {
vetorDados.add(new RegistroPessoa());
fireTableRowsInserted(vetorDados.size() - 1, vetorDados.size() - 1);
}
}
Em seguida a classe de formulário (JFrame, Listagem 3) deverá ser criada para fazer as chamadas ao modelo interativo.
package br.edu.devmedia.jtable;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
/**
* Classe que produz o form e o gerencia
*/
public class FormInterativo extends JPanel {
private static final long serialVersionUID = 1L;
public static final String[] nomeColunas =
{ "Nome", "Endereço", "Fone", "" };
protected JTable table;
protected JScrollPane scroller;
protected TableModelInterativo modeloInterativo;
public FormInterativo() {
iniciarComponentes();
}
public void iniciarComponentes() {
modeloInterativo = new TableModelInterativo(nomeColunas);
modeloInterativo.addTableModelListener(new FormInterativo.TableModelListenerInterativo());
table = new JTable();
table.setModel(modeloInterativo);
table.setSurrendersFocusOnKeystroke(true);
if (!modeloInterativo.hasLinhasVazias()) {
modeloInterativo.addLinhaVazia();
}
scroller = new javax.swing.JScrollPane(table);
table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300));
// Essa coluna ficará escondida até que o Tab seja dado
TableColumn colunaEscondida = table.getColumnModel().getColumn(TableModelInterativo.INDEX_ESCONDIDO);
colunaEscondida.setMinWidth(2);
colunaEscondida.setPreferredWidth(2);
colunaEscondida.setMaxWidth(2);
colunaEscondida.setCellRenderer(new InteractiveRenderer(TableModelInterativo.INDEX_ESCONDIDO));
setLayout(new BorderLayout());
add(scroller, BorderLayout.CENTER);
}
public void destacarUltimaLinha(int linha) {
int ultimaLinha = modeloInterativo.getRowCount();
if (linha == ultimaLinha - 1) {
table.setRowSelectionInterval(ultimaLinha - 1, ultimaLinha - 1);
} else {
table.setRowSelectionInterval(linha + 1, linha + 1);
}
table.setColumnSelectionInterval(0, 0);
}
/**
* Classe interna para rendereizar as interações
*/
private class InteractiveRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
protected int colunaInterativa;
public InteractiveRenderer(int colunaInterativa) {
this.colunaInterativa = colunaInterativa;
}
public Component getTableCellRendererComponent(JTable table,
Object valor, boolean isSelecionado, boolean hasFoco, int linha,
int coluna) {
Component c =
super.getTableCellRendererComponent(table, valor,
isSelecionado, hasFoco, linha, coluna);
if (coluna == colunaInterativa && hasFoco) {
if ((FormInterativo.this.modeloInterativo.getRowCount() - 1) == linha
&& !FormInterativo.this.modeloInterativo.hasLinhasVazias()) {
FormInterativo.this.modeloInterativo.addLinhaVazia();
}
destacarUltimaLinha(linha);
}
return c;
}
}
/**
* Ouvinte para o modelo interativo
*/
public class TableModelListenerInterativo implements TableModelListener {
/**
* Método que recebe os valores de linha modificados
*/
public void tableChanged(TableModelEvent evt) {
if (evt.getType() == TableModelEvent.UPDATE) {
int column = evt.getColumn();
int row = evt.getFirstRow();
table.setColumnSelectionInterval(column + 1, column + 1);
table.setRowSelectionInterval(row, row);
}
}
}
/*
* Método main
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
JFrame frame = new JFrame("Formulário Interativo");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
frame.getContentPane().add(new FormInterativo());
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
A fim de se obter o efeito de se mover o cursor da esquerda para a coluna da direita, nós adicionamos um InteractiveTableModelListener para ouvir todas as atualizações feitas no interior das células. Você pode visualizar o uso do mesmo através do método “tableChanged()”, na Listagem 3. Além disso, a linha que contém a chamada ao método “setSurrendersFocusOnKeystroke()” deve ser adicionada depois de inicializar nossa JTable.
Precisamos fazer com que a largura da coluna da tabela de INDEX_ESCONDIDO seja tão pequena quanto possível e ao mesmo tempo a célula da tabela renderizadora também poderia ser chamada quando ele recebe o foco da célula:
A Coluna INDEX_ESCONDIDO atuará como uma célula manequim para interceptar qualquer foco que recebe e em troca ela irá adicionar automaticamente uma nova linha abaixo dele, se ele já não tiver um.
A principal dica para passar da linha superior para a próxima linha é encontrada na classe InteractiveRenderer, que contempla todo o código para tal (Listagem 3).
No final, o resultado da tabela pode ser visualizado na Figura 1.
Conclusão
Nesse artigo aprendemos a criar uma JTable interativa em java usando a tecnologia Swing, caso tenham tido alguma dúvida, fiquem a vontade em usar os comentários, espero que tenham gostado e até o próximo artigo.