Tutorial sobre JTable - Parte 2: Implementando Seu Modelo

Bruno Rios Lima

Nesta parte iremos criar o nosso próprio modelo de tabela e aprenderemos a trabalhar com ele.

Download do material relacionado ao tutorial



Tutorial sobre JTable - Parte 2: Implementando Seu Modelo


Bem vindo à parte 2 do tutorial sobre JTable. O primeiro artigo voce encontra clicando aqui.Nesta parte iremos criar o nosso próprio modelo de tabela e aprenderemos a trabalhar com ele.

Implementado o TableModel

No nosso primeiro tutorial foi dado uma pequena palinha de como uma implementação de um TableModel deve ser feita, utilizando a classe abstrata AbtractTableModel.
Se não se lembra, a classe abstrata AbstractTableModel implementa diversas funcionalidades da TableModel: incluindo os eventos que disparam para a JTable alterações ocorridas nos dados do modelo. Estes métodos estão disponíveis para uso nas subclasses, pois são métodos internos do modelo. Agora, com relação aos métodos que tratam os dados propriamente ditos, não é implementado nenhum método: isso significa que você deverá implementar do melhor jeito ( pelo menos que você ache ) de armazenar os dados que serão exibidos pela JTable e que o modelo irá fornecer a ela.

O modelo de dados funciona como um repositório dos dados, portanto ele deve armazenar-los em algum lugar: nada melhor do que um array para armazenas o conteúdo das linhas. Agora, imagine que, em cada linha, podemos ter 2 colunas. Como armazenar 2 colunas dentro de um array? Podemos usar um array de String de tamanho 2, por exemplo. Ou até mesmo outro array do tipo java.util.List. Por ser um conteúdo dinâmico ( que pode ser alterado em tempo de execução: exemplo remover ou incluir novas linhas ) , nunca podemos saber o tamanho exato maximo que esse array dos dados terá. Por isso, o melhor jeito é ter uma Collection para armazena-lo. Neste exemplo vamos usar a classe java.util.ArrayList para armazenar os dados da linha e um array de String para colunas.

Primeiro, vamos criar a classe e suas variáveis internas que irão guardar o dados do nosso modelo:

01 public class SimpleTableModel extends AbstractTableModel{
02 
03     private ArrayList linhas = null;
04     private String [] colunas = null;
05     public String[] getColunas() {return colunas;}
06     public ArrayList getLinhas() {return linhas;}
07     public void setColunas(String[] strings) {colunas = strings;}
08     public void setLinhas(ArrayList list) {linhas = list;}
09 
10 }


Agora que temos onde guardar os dados que queremos exibir, devemos implementar os métodos que fornecem a JTable os dados suficientes para ela saber montar a parte visual: o numero de colunas, numero de linhas e os valores para preencher as células:

01 /**
02  * Retorna o numero de colunas no modelo
03  @see javax.swing.table.TableModel#getColumnCount()
04  */
05 public int getColumnCount() {return getColunas().length;}
06 
07 /**
08  * Retorna o numero de linhas existentes no modelo
09  @see javax.swing.table.TableModel#getRowCount()
10  */
11 public int getRowCount() {return getLinhas().size();}
12 
13 /**
14  * Obtem o valor na linha e coluna
15  @see javax.swing.table.TableModel#getValueAt(int, int)
16  */
17 public Object getValueAt(int rowIndex, int columnIndex) {
18     // Obtem a linha, que é uma String []
19     String [] linha = (String [])getLinhas().get(rowIndex);
20     // Retorna o objeto que esta na coluna
21     return linha[columnIndex];
22 }


Agora, devemos criar o construtor da classe que irá receber os dados e as colunas que desejamos.

1 public SimpleTableModel(ArrayList dados, String[] colunas){
2     setLinhas(dados);
3     setColunas(colunas);
4 }




A partir disto, já temos o nosso modelo pronto para exibir os dados contido nele. Agora vamos criar uma apresentação simples utilizando este nosso modelo. Vamos criar algo parecido com a do exemplo da primeira parte deste tutorial, e usaremos o mesmo nome: MyTableViewer. Abaixo segue como deve ficar o método que cria a JTable :

01 /**
02  * Cria a JTable
03  @return
04  */
05 public JTable createJTable() {
06 
07     ArrayList dados = new ArrayList();
08     String[] colunas = new String[] { "Estado""Cidade" };
09 
10     // Alimenta as linhas de dados
11     dados.add(new String[] { "SP""São Paulo" });
12     dados.add(new String[] { "RJ""Rio de Janeiro" });
13     dados.add(new String[] { "RN""Rio Grande do Norte" });
14     dados.add(new String[] { "ES""Espirito Santo" });
15 
16     SimpleTableModel modelo = new SimpleTableModel(dados, colunas);
17     JTable jtable = new JTable(modelo);
18     jtable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
19 
20     return jtable;
21 
22 }


Observe que o método acima alimenta as variáveis: dados agora é um ArrayList, e incluímos nele diversos arrays de String , pois é o esperado dentro da linha ( veja a implementação do método getValueAt ).
Se rodarmos o código agora, ele irá apresentar a Jtable com nossos dados, e não irá permitir alterar, diferentemente do modelo DefaultTableModel. Isso porque não implementamos o método isCellEditable, e ele acabou herdando do AbstractTableModel, que retorna false para todas as células. No default, este método retorna true.


Alterando o Conteúdo Do Modelo:
Para permitir a edição da célula, e, principalmente, que seu valor venha parar aqui no modelo ( se você implementar somente o método isCellEditable sem o setValueAt, simplesmente ele permite a edição, mas não "fixa" seu valor na célula, pois ele não vem para o seu modelo não refletindo de volta em sua JTable ) devemos implementar os seguintes métodos:

1   public boolean isCellEditable(int row, int col)
2   public void setValueAt(Object value, int row, int col)


Você pode deixar separado o controle de edição de suas células, permitindo em tempo de execução quando determinada célula irá permitir alteração. Neste exemplo vamos permitir que seja informado, na criação do modelo, quais colunas vamos permitir edição e quais não. Para isso, vamos ter uma variável que é um array de boolean que guardará o valor de cada coluna.

1 private boolean [] colsEdicao;
2 public boolean isCellEditable(int row, int col){
3     return colsEdicao[col];
4 }


E vamos alterar o construtor para receber esse novo parâmetro:

1 public SimpleTableModel(ArrayList dados, String[] colunas, boolean [] edicao){
2     setLinhas(dados);
3     setColunas(colunas);
4     colsEdicao = edicao;
5 }


O método para setar o valor, nada mais faz do que obter o objeto na linha e coluna e altera-lo. Porém, devemos lembrar que quando é feito uma alteração do conteúdo do modelo, devemos informar a JTable. Fazemos isso atraves do método fireTableCellUpdated.

01 /**
02  * Seta o valor na linha e coluna
03  @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
04  */
05 public void setValueAt(Object value, int row, int col){
06     // Obtem a linha, que é uma String []
07     String [] linha = (String [])getLinhas().get(row);
08     // Altera o conteudo no indice da coluna passado
09     linha[col(String)value;
10     // dispara o evento de celula alterada
11     fireTableCellUpdated(row,col);
12 }


Para visualizarmos as alterações que permitem alterar o valor da célula na JTable, devemos fazer as seguintes alterações na apresentação ( MyTableViewer ) no método de criação da JTable:

1  MyTableViewer.createJTable()
2 
3 String[] colunas = new String[] { "Estado""Cidade" }// sem alterçao
4 // linha adicionada apos a criação das colunas
5 boolean [] edicao = {false, true};
6 
7 // Alterar chamada de criação do modelo
8 SimpleTableModel modelo = new SimpleTableModel(dados, colunas, edicao);
9 // para enviar as propriedades de edicao das colunas





Inclusao de novas linhas

Como a jtable não faz inclusão de linhas no modelo diretamente, este método não é uma implementação, ou seja, não é preciso ter uma assinatura especifica.
Para incluir novas linhas, devemos adicionar um novo objeto no array de linhas. Este objeto deve ser do mesmo tipo em todas as linhas, ou seja, se for utilizar um array de String[2], sempre incluir arrays de String[2]. Pois no método getValueAt você tem que saber o que pegar nas linhas.
Abaixo segue um exemplo de um método, bem simples, para incluir uma nova linha no modelo.

1 public void addRowString [] dadosLinha){
2     getLinhas().add(dadosLinha);
3     // Informa a jtable de que houve linhas incluidas no modelo
4     // COmo adicionamos no final, pegamos o tamanho total do modelo
5     // menos 1 para obter a linha incluida.
6     int linha = getLinhas().size()-1;
7     fireTableRowsInserted(linha,linha);
8     return;
9 }


Exclusao de linhas existentes

Assim como a inclusão de linhas, a exclusão delas é feita diretamente pelo modelo dos dados da jtable. E funciona assim como na inclusão. Devemos remover a linha passada pelo parâmetro. Usando um ArrayList, podemos excluir diretamente pelo indice dele. Ou, podemos esperar um campo contido na linha, fazemos um looping nos dados para verificar o conteúdo.
Abaixo, veremos esses dois exemplos de exclusão.
Um problema que devemos tomar cuidado é que, se informado uma linha que não existe no modelo, então o método remove do ArrayList irá disparar um IndexOutOfBound, ou seja, não existe indice informado dentro do array.
Você pode tratar isso verificando se o parâmetro informado irá passar dos limites do array ( se ele é negativo e se é maior ou igual ao tamanho total do array ).

01 public void removeRow(int row){
02     getLinhas().remove(0);
03     // informa a jtable que houve dados deletados passando a 
04     // linha reovida
05     fireTableRowsDeleted(row,row);
06 }
07 
08 
09 /**
10  * Remove a linha pelo valor da coluna informada
11  @param val
12  @param col
13  @return
14  */
15 public boolean removeRow(String val, int col){
16     // obtem o iterator
17     Iterator i = getLinhas().iterator();
18     int linha = 0;
19     // Faz um looping em cima das linhas
20     while(i.hasNext()){
21         // Obtem as colunas da linha atual
22         String[] linhaCorrente = (String[])i.next();
23         linha++;
24         // compara o conteudo String da linha atual na coluna desejada
25         // com o valor informado
26         iflinhaCorrente[col].equals(val) ){
27             getLinhas().remove(linha);
28             // informa a jtable que houve dados deletados passando a 
29             // linha removida
30             fireTableRowsDeleted(linha,linha);
31             return true;                
32         }
33     }
34     // Nao encontrou nada
35     return false;
36 }




Retornando o Titulo da Coluna

Este método retorna uma String com o titulo da coluna. A JTable irá chamar este método para obter o titulo da coluna e colocar no Header, quando suas colunas forem criadas automaticamente a partir do seu modelo.

01 // Implementação do getColumnName
02 
03 /**
04  * Retorna o nome da coluna.
05  @see javax.swing.table.TableModel#getColumnName(int)
06  */
07 public String getColumnName(int col){
08     return getColunas()[col];
09 }
10    


Para evitar que a JTable crie as colunas nela automaticamente, devemos setar a propriedade AutoCreateColumnsFromModel para false. Neste caso, devemos criar as colunas na mão.


Conclusão

Tenha em mente uma coisa: dentro do modelo, não importa como você armazene seus dados. Não interessa para a JTable se você usou a classe mais avançada, se buscou na hora direto do seu banco, ou se está tudo em um array. O importante é fornecer o valor atraves dos métodos, e informar das alterações ocorridas no seu modelo.
Assim, você poderá implementar de diversas formas o seu modelo. Futuramente iremos trabalhar usando os Renderers e Editors. Com eles, poderemos utilizar qualquer classe para exibir ou armazenar e logo veremos a vantagem de se implementar o seu próprio modelo.


No proximo tutorial, iremos ver como criar as colunas direto na JTable, sem deixar que elas sejam criadas automaticamente pelo modelo. Tambem iremos ver como criar nossos próprios renderers e editors para usar na tabela. Aguardem!


Abraços e até o proximo tutorial!


Copyright © 2002-2006 GUJ | Todas as marcas e marcas registradas que aparecem no GUJ são de propriedade de seus respectivos donos