Exemplo de TableModel

35 respostas
E

Tive que criar uma tela com JTable no curso que estou fazendo e implementei também um TableModel. Como procurei deixar o código comentado, achei interessante postar pro pessoal daqui.

O TableModel está bem simples e implementado para exibir dados de uma classe Socio. Estou anexando também uma classe JFrame que fiz só para teste mesmo.

Pra quem se interessar e achar algum erro ou quiser dar uma sugestão de melhoria, os comentários serão muito bem vindos.

Edit: O método getSocio estava com retorno void, o erro foi corrigido.

O sócio foi modificado, recebendo campos com data e valor numérico. Assim fica visível que a JTable formata automaticamente os dados dependendo do tipo.

Post com explicação passo a passo: http://devsv.wordpress.com/2012/07/08/como-implementar-um-tablemodel/.

Falou…

35 Respostas

V

Ficou excelente!

Morte ao DefaultTableModel! \o/

M

Belo exemplo!

Morte ao DefaultTableModel! \o/

E

Valeu pelos comentários.

Praticamente tudo que sei sobre Table Model, eu devo a assinatura de vocês. :smiley:

E valeu Vini. Seus posts e exemplos sempre me ajudam muito.

Morte ao DefaultTableModel! \o/

T

morte ao tableModel ?!??!?!?!

desculpe a ignorância mais como eu faço minha própria table? (sou leigo ainda!!)

pra aparecer lah na form certinho se nao for arrastando jtable?

vlw

M

thayson:
morte ao tableModel ?!??!?!?!

desculpe a ignorância mais como eu faço minha própria table? (sou leigo ainda!!)

pra aparecer lah na form certinho se nao for arrastando jtable?

vlw

Morte ao DefaultTableModel, não o TableModel.

A JTable faz seu papel muito bem, o importante é saber como os dados vão aparecer na tabela.

T

Ola galera! estou seguinto a orientaçao de vcs em usar o TableModel ao invé do DefautTableModel.
porem to com um problema quando insiro o valor na tabela. ele da o seguinte erro:

<blockquote>Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException

at javax.swing.JTable.prepareRenderer(Unknown Source)

at javax.swing.plaf.basic.BasicTableUI.paintCell(Unknown Source)

at javax.swing.plaf.basic.BasicTableUI.paintCells(Unknown Source)

at javax.swing.plaf.basic.BasicTableUI.paint(Unknown Source)

at javax.swing.plaf.ComponentUI.update(Unknown Source)

at javax.swing.JComponent.paintComponent(Unknown Source)

at javax.swing.JComponent.paint(Unknown Source)

at javax.swing.JComponent.paintToOffscreen(Unknown Source)

at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)

at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)

at javax.swing.RepaintManager.paint(Unknown Source)

at javax.swing.JComponent._paintImmediately(Unknown Source)

at javax.swing.JComponent.paintImmediately(Unknown Source)

at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)

at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)

at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)

at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)

at java.awt.event.InvocationEvent.dispatch(Unknown Source)

at java.awt.EventQueue.dispatchEventImpl(Unknown Source)

at java.awt.EventQueue.access$000(Unknown Source)

at java.awt.EventQueue$1.run(Unknown Source)

at java.awt.EventQueue$1.run(Unknown Source)

at java.security.AccessController.doPrivileged(Native Method)

at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)

at java.awt.EventQueue.dispatchEvent(Unknown Source)

at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)

at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)

at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)

at java.awt.EventDispatchThread.pumpEvents(Unknown Source)

at java.awt.EventDispatchThread.pumpEvents(Unknown Source)

at java.awt.EventDispatchThread.run(Unknown Source)</blockquote>

tentei usar o exemplo do colega Eric Yuzo

a diferença do dele pro meu é q eu quero adicionar na tabela valores q estao nos campos de entrada. e no dele o valores ja estao inseridos. porem o meu ta apresentando esse erro, gostaria de saber se vcs sabem oq é.

E

Poste o trecho do código que está inserindo o valor no TableModel.

A propósito, o TableModel do exemplo não tem nenhum valor setado por padrão. A lista de sócios é populada na tela de teste.

T

Eric Yuzo:
Poste o trecho do código que está inserindo o valor no TableModel.

A propósito, o TableModel do exemplo não tem nenhum valor setado por padrão. A lista de sócios é populada na tela de teste.

entendi…

e tb consegui consertar oq eu precisava, era um erro bobo! agora ta funcionando de boa! fico muito agradecido ao Eric Yuzo, com sua dica consegui entender… e concluí q é muito melhor usar TableModel. valew galera obrigado… caso queiram ver oq eu tava fazendo é so falar!

T

Aeh galera… mais outra pergunta!

oq é melhor trabalhar com hashMap ou ArrayList ? Eu vi exemplos de codigos com hashMap porém nao entendi! e gostaria de saber a opniao de vcs!!

E

Para o TableModel, use uma lista mesmo.

Na lista você guarda os objetos que representam as linhas e que podem ser acessados pelo índice. Lembre-se que a JTable, na hora de renderizar as células, busca o valor pelo índice e não por uma chave.

D

Ótima implementação, ficou incrível.

:smiley:

S

Então galera, já fazem muitos anos que venho acompanhando o GUJ, acho que desde 2003 quando entrei na faculdade.
Na área de programação já é quase 10 anos, tenho muita intimidade com java swing, e acho que posso dar um parecer técnico ao meu ponto de vista.

O DefaultTabelModel não pode, nem deve ser morto, ele é o feijão com arroz, o básico onde as pessoas usam sem entender para fazer funcionar o básico de uma tabela.

Quando a pessoa passa por uma situação diferente tipo colocar imagem, jProgressBar dentro da tabela, a pessoa estuda o caso mais a fundo.
Ai sim passa a entender o funcionamento de um model e vai implementar um com esses exemplos todos que estão rolando por aqui no forum.

É ótimo os exemplos, eu os uso, mas para quem esta no javaBaby, tem que começar de algo básico e ir progredindo.

Abraços!

V

SneepS:
Então galera, já fazem muitos anos que venho acompanhando o GUJ, acho que desde 2003 quando entrei na faculdade.
Na área de programação já é quase 10 anos, tenho muita intimidade com java swing, e acho que posso dar um parecer técnico ao meu ponto de vista.

O DefaultTabelModel não pode, nem deve ser morto, ele é o feijão com arroz, o básico onde as pessoas usam sem entender para fazer funcionar o básico de uma tabela.

Quando a pessoa passa por uma situação diferente tipo colocar imagem, jProgressBar dentro da tabela, a pessoa estuda o caso mais a fundo.
Ai sim passa a entender o funcionamento de um model e vai implementar um com esses exemplos todos que estão rolando por aqui no forum.

É ótimo os exemplos, eu os uso, mas para quem esta no javaBaby, tem que começar de algo básico e ir progredindo.

Abraços!

E quem paga o custo da manutenção das tabelas deixadas com o DefaultTableModel?

No fundo, usa-lo é ferir bons modelos, como o MVC. É ter trabalho extra à toa e aumentar os custos de produção e manutenção do sistema.
Você não sai por aí recomendando que as pessoas não dividam o software em camadas, sai?

Ou dizendo que programadores júniores, por serem júniores, tem o direito de sair fazendo sistema de qualquer jeito, diz?

É a mesma coisa com o DefaultTableModel. Estamos falando em aboli-lo de desenvolvimento profissional. Acho impressionante que muita gente aqui gasta esforço real criando DAOs, Controllers, dividindo a View mas, não sei por que, chega na hora do TableModel, fica resistente em aprender algo tão simples. Já vi mais de um caso desse no GUJ.

Para fins didáticos, ele pode até ser usado como exemplo, mas só. O problema é quando algum recurso didático é tão pobre, e tão ruim, que você no fundo está deseducando ao invés de educar.

S

ViniGodoy:
SneepS:
Então galera, já fazem muitos anos que venho acompanhando o GUJ, acho que desde 2003 quando entrei na faculdade.
Na área de programação já é quase 10 anos, tenho muita intimidade com java swing, e acho que posso dar um parecer técnico ao meu ponto de vista.

O DefaultTabelModel não pode, nem deve ser morto, ele é o feijão com arroz, o básico onde as pessoas usam sem entender para fazer funcionar o básico de uma tabela.

Quando a pessoa passa por uma situação diferente tipo colocar imagem, jProgressBar dentro da tabela, a pessoa estuda o caso mais a fundo.
Ai sim passa a entender o funcionamento de um model e vai implementar um com esses exemplos todos que estão rolando por aqui no forum.

É ótimo os exemplos, eu os uso, mas para quem esta no javaBaby, tem que começar de algo básico e ir progredindo.

Abraços!

E quem paga o custo da manutenção das tabelas deixadas com o DefaultTableModel?

No fundo, usa-lo é ferir bons modelos, como o MVC. É ter trabalho extra à toa e aumentar os custos de produção e manutenção do sistema.
Você não sai por aí recomendando que as pessoas não dividam o software em camadas, sai?

Ou dizendo que programadores júniores, por serem júniores, tem o direito de sair fazendo sistema de qualquer jeito, diz?

É a mesma coisa com o DefaultTableModel. Estamos falando em aboli-lo de desenvolvimento profissional. Acho impressionante que muita gente aqui gasta esforço real criando DAOs, Controllers, dividindo a View mas, não sei por que, chega na hora do TableModel, fica resistente em aprender algo tão simples. Já vi mais de um caso desse no GUJ.

Para fins didáticos, ele pode até ser usado como exemplo, mas só. O problema é quando algum recurso didático é tão pobre, e tão ruim, que você no fundo está deseducando ao invés de educar.

Ai sim Vini, quem atua profissionalmente deve aprender, ir a fundo no que esta fazendo, não simplesmente copiar o codigo de alguem, deve compreender o que o codigo esta fazendo e deve se possivel melhora-lo com as melhores práticas da programação como estas que tu citou.

Mas, como eu disse, aprender é um passo de cada vez, ir aumentando o nivel da programação gradualmente, quem esta na fase de primeiro contato com JAVA, mal entende orientação a objeto, muito menos o conceito de MVC.

Mas, você me convenceu…rsrsr. Se no primeiro contato com uma table o cara já aprende-se junto a implementar um model seria um “adiantamento” na vida de quem esta aprendendo.

B

Finalmente consegui entender quando me diziam "você deve construir um modelo para tabela primeiro"
Com base nos teus codigos, consegui desenvolver um modelo para uma tabela que vou usar depois numa janela, ficou muito bom, testei criando uma classe parecida com o seu que testa o modelo, e funcionou muito melhor do que eu esperava.

Parece que vou entrar para esse grupo dos Morte ao DefaultTableModel!

E

Blaquicat:
Finalmente consegui entender quando me diziam "você deve construir um modelo para tabela primeiro"
Com base nos teus codigos, consegui desenvolver um modelo para uma tabela que vou usar depois numa janela, ficou muito bom, testei criando uma classe parecida com o seu que testa o modelo, e funcionou muito melhor do que eu esperava.

Parece que vou entrar para esse grupo dos Morte ao DefaultTableModel!


Valeu. Fico feliz que o exemplo tenha auxiliado no entendimento. :smiley:

Atualizei o primeiro post, adicionando um link para um artigo com uma explicação melhor sobre o assunto: http://devsv.wordpress.com/2012/07/08/como-implementar-um-tablemodel/.

V

Atualizei o post que o link da minha assinatura referencia para incluir esse seu artigo também. Ficou muito bom!

E

Obrigado pela referência, ViniGodoy.

H

Pessoal,

segue exemplo simples de um TableModel “genérico”. Talvez ajude alguém:

public abstract class SimpleTableModel<T> implements TableModel {
    
    protected List<String> columnNames;
    protected List<Class> columnTypes;
    private List<TableModelListener> listeners;

    public SimpleTableModel(Class<T> type) {
        columnNames = new ArrayList<String>();
        columnTypes = new ArrayList<Class>();
        listeners = new ArrayList<TableModelListener>();
        initFromType(type);
    }
    
    protected void initFromType(Class<T> type) {
        
        Method[] metodos = type.getMethods();
        
        Map<String, Class> columns = new HashMap<String, Class>();
        
        for (Method m : metodos) {
            
            if((m.getName().startsWith("get") || m.getName().startsWith("is")) && ! m.getName().equals("getClass")) {
                
                int offset = m.getName().startsWith("get") ? 3 : 2;
                
                columnNames.add(m.getName().substring(offset));
                columnTypes.add(m.getReturnType());
                
            }
            
        }
        
    }

    protected abstract List<T> getValues();

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        
        if(columnIndex < 0 || columnIndex >= columnTypes.size()) {
            throw new IndexOutOfBoundsException("Índice " + columnIndex + " inválido");
        }
        
        if(rowIndex < 0 || rowIndex >= getValues().size()) {
            throw new IndexOutOfBoundsException("Índice " + rowIndex + " inválido");
        }
        
        T value = getValues().get(rowIndex);
        
        Class type = value.getClass();
        
        Method[] allMethods = type.getMethods();
        
        //List<Method> setters = new ArrayList<Method>();
        Map<String, Method> setters = new HashMap<String, Method>();
        List<Method> getters = new ArrayList<Method>();
        
        for (Method m : allMethods) {
            
            if(m.getName().startsWith("set")) {
                
                setters.put(m.getName().substring(3), m);
                
            } else if((m.getName().startsWith("get") || m.getName().startsWith("is")) && ! m.getName().equals("getClass")) {
                getters.add(m);
            }
            
        }
        
        String methodName = getters.get(columnIndex).getName();
        
        if(methodName.startsWith("is")) {
            methodName = methodName.substring(2);
        } else {
            methodName = methodName.substring(3);
        }
        
        T object = getValues().get(rowIndex);
        
        try {
            Method setter = setters.get(methodName);
            setter.invoke(object, aValue);
            fireTableChange(new TableModelEvent(this));
        } catch (Exception e) {
            throw new RuntimeException("Falha ao definir valor na tabela", e);
        }
        
    }
    
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        
        if(columnIndex < 0 || columnIndex >= columnTypes.size()) {
            throw new IndexOutOfBoundsException("Índice " + columnIndex + " inválido");
        }
        
        if(rowIndex < 0 || rowIndex >= getValues().size()) {
            throw new IndexOutOfBoundsException("Índice " + rowIndex + " inválido");
        }
        
        T value = getValues().get(rowIndex);
        
        Class type = value.getClass();
        
        Method[]  allMethods = type.getMethods();
        
        List<Method> methods = new ArrayList<Method>();
        
        for (Method m : allMethods) {
            if((m.getName().startsWith("get") || m.getName().startsWith("is")) && ! m.getName().equals("getClass")) {
                methods.add(m);
            }
        }
        try {
            return methods.get(columnIndex).invoke(value);
        } catch (Exception ex) {
            throw new RuntimeException("Falha ao pegar valor em célula da tabela", ex);
        } 
        
    }
    
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        
        if(columnIndex < 0 || columnIndex >= columnTypes.size()) {
            throw new IndexOutOfBoundsException("Índice " + columnIndex + " inválido");
        }
        
        return columnTypes.get(columnIndex);
        
    }

    @Override
    public String getColumnName(int columnIndex) {
        if(columnIndex < 0 || columnIndex >= columnNames.size()) {
            throw new IndexOutOfBoundsException("Índice " + columnIndex + " inválido");
        }
        
        return columnNames.get(columnIndex);
        
    }

    @Override
    public int getColumnCount() {
        return columnNames.size();
    }
    
    @Override
    public void addTableModelListener(TableModelListener l) {
        listeners.add(l);
    }

    @Override
    public void removeTableModelListener(TableModelListener l) {
        listeners.remove(l);
    }
    
    protected void fireTableChange(TableModelEvent evt) {
        for (TableModelListener l : listeners) {
            l.tableChanged(evt);
        }
    }
    
}

Infelizmente, esse esquema força que as implementações desse TableModel sempre o construam passando a classe do tipo manipulado! Existe jeito de não precisar disso?
Ps. Não dei atenção para o disparo de eventos… Pode ser melhor definido.

Obrigado e abraços!

H

Ops! Faltou o método getRowCount:

@Override
public int getRowCount() {
    return getValues().size();
}

Abraços!

V

Dê uma olhada no ObjectTableModel do projeto Towel. Ele usa reflexão para identificar o local e tipo dos objetos:

H

Obrigado, ViniGodoy!

L

Post muito bom!! :smiley:

B

Fiz o meu TableModel baseado nesse do Eric Yuzo. Impressionante como facilita a vida!

C

o.O

Salvou minha vida em 2013!

E como disse o ViniGodoy:
----> Morte ao DefaultTableModel! \o/

Aê Vini, isso se aplica ao DefaultListModel tbm, certo???

L

Uma dúvida, se eu fizer isso: jTable.setValueAt(“bla bla bla”, 0, 1); ou jTable.getValueAt(0, 1);
eu vou chamar os métodos setValueAt e getValueAt do TableModel que implementei?

M

Eu tenho uma pergunta… eu Posso criar uma Interface tablemodel exemplo…

configurando toda a table model e etc… e depois que eu for configurar uma classe para salvar os dados em uma table.

eu extender para essa interface criada?

G

Eric Yuzo,
Eu usei o teu exemplo de TableModel, antes mesmo de tentar o DefaultTableModel, e é bem simples mesmo. Mas eu to com um problema, como que eu posso alterar a largura de cada coluna?

V

gpaschoaletto:
Eric Yuzo,
Eu usei o teu exemplo de TableModel, antes mesmo de tentar o DefaultTableModel, e é bem simples mesmo. Mas eu to com um problema, como que eu posso alterar a largura de cada coluna?

Isso não tem a ver com o TableModel, que é o modelo de onde vem os dados da tabela, e sim, com o modelo das colunas. Você teria que fazer algo como:

table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); table.getColumnModel().getColumn(0).setMinWidth(100);

G

Foi, valeu!

PS: pelo menos umas 10 respostas suas me ajudaram hoje xD

V

gpaschoaletto:
Foi, valeu!

PS: pelo menos umas 10 respostas suas me ajudaram hoje xD

:wink:

N

Olá Vini, uma dúvida, depois que fizer o meu table model, para preencher com os dados do banco é o mesmo do Jtable, tipo do netbeans?

V

O TableModel é a classe que explica para o JTable quais dados devem ser preenchidos.

N

Vini desculpe minha ignorância, mas não entendi esse negócio ainda não, estou quebrando a cabeça e não entendo, se importaria em expor um ex pequeno se baseando numa conexão, vou colocar a minha conexão abaixo pra você ver como faço, claro que tem mais formas de conexão, mas essa a que eu me acho mais fácil e sou muito leigo ainda mas gostaria muito de implementar o AbstractTableModel.

Minha conexão:

package utilitarios;
import java.sql.*;
import javax.swing.*;

public class conexao
{
          final private String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
          final private String url = "jdbc:odbc:EstoqueNetbeans";
          final private String usuario = "";
          final private String senha = "";
          /*String driver = "org.postgresql.Driver";
          String url = "jdbc:postgresql://localhost:5432/estoque"; 
          String usuario = "postgres"; 
          String senha   = "1203";*/ 
          private Connection conexao;
          public Statement statement;
          public ResultSet resultset;
              
          public boolean conecta()
              {
                 boolean result = true;                
                 try    
                 {             
                    Class.forName(driver);
                    conexao = DriverManager.getConnection(url, usuario, senha);
                    
                 }
                 catch(ClassNotFoundException Driver) 
                 {
                    JOptionPane.showMessageDialog(null,"Driver não localizado: "+Driver);
                    result = false;
                 }
                 catch(SQLException Fonte) 
                {
                    JOptionPane.showMessageDialog(null,"Deu erro na conexão "+
                            "com a fonte de dados: "+Fonte);
                    result = false;
                }
                 return result;
             }
          public void desconecta()
          {
              boolean result = true;
              try
              {
                  conexao.close();
                  JOptionPane.showMessageDialog(null,"Banco Fechado");
              }
              catch(SQLException fecha)
              {
                  JOptionPane.showMessageDialog(null,"Não foi pssível"+
                          "fechar o banco de dados:"+fecha);
              }
          }
          
          public void executeSQL(String sql)
          {
              try
              {
                  statement = conexao.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
				     ResultSet.CONCUR_READ_ONLY);
                  resultset =statement.executeQuery(sql);
              }
              catch(SQLException sqlex)
              {
                  JOptionPane.showMessageDialog(null,"Não foi possível"+
                          "executar o comando sql,"+sqlex+", o sql passado foi"+sql);
              }
          }    

}

Onde Chamo a conexão:

conexao con_produto;

        con_produto = new conexao();
        con_produto.conecta();
V

São coisas diferentes. Uma classe de banco de dados tem o papel de ler os dados do banco e gerar uma lista de objetos de algum tipo. Por exemplo, você poderia criar uma classe chamada ClienteDAO (DAO de Data Access Object) que rodasse um resultset e gerasse uma lista de objetos do tipo Cliente.

Por exemplo:

package utilitarios;
import java.util.*;

public class ClienteDao
{
      public List&lt;Cliente&gt; carregarClientes() {
            conexao con = new conexao();
            if (!con.conecta()) {
                return Collections.emptyList();
            }
            con.executeSQL("SELECT * FROM Cliente ORDER BY nome");
            ResultSet rs = con.getResultSet();
            List&lt;Cliente&gt; clientes = new ArrayList&lt;&gt;();
            while (rs.next()) {
                 Cliente cli = new Cliente();
                 cli.setNome(rs.getString(&quot;nome&quot;));
                 cli.setMatricula(rs.getInt(&quot;matricula&quot;));
                 clientes.add(cliente);
            }
            return clientes;
    }
}

A lista de clientes é que vai para o TableModel. Os tutoriais na assinatura mostra como preencher uma tabela com base numa lista de um objeto de uma classe.

Criado 26 de novembro de 2010
Ultima resposta 10 de jun. de 2014
Respostas 35
Participantes 16