JTable Interativa com Swing

Nesse artigo veremos como criar uma JTable interativa em Java usando Swing.

É 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; } }
Listagem 1. Classe RegistroPessoa.java

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); } }
Listagem 2. Classe TableModelInterativo.java
Nota: A constant INDEX_ESCONDIDO servirá para guardar uma coluna para uso posterior.

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(); } } }
Listagem 3. Classe FormInterativo.java

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.

Figura 1. Tela de Formulário Interativo

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.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados