JTable Removendo colunas em tempo de execução

Bem gente, é isso mesmo, to tentando implementar na minha JTable a possibilidade de o usuário poder adicionar e remover colunas em tempo de execução. Adicionar já consegui implementar, porém to tendo problemas para remover, tipo, eu até consigo remove-la porém quando adiciono a mesma coluna novamente ela aparece duplicada, ou sejá, ela adiciona duas vezes a coluna, como se ao remover a coluna ela não fosse realmente removida mas apenas escondida, ai ele adiciona uma nova coluna e reexibe a que foi excluida.

abaixo segue os códigos

metodo adiciona coluna

    public static void adicionarColunas(JTable tabela, DefaultTableModel modelo, Object titulo,
            String colunBD, String colChave, ResultSet resultConsu, ResultSet resultCont){

        TableColumn coluna = new TableColumn(modelo.getColumnCount());
        coluna.setHeaderValue(titulo);

        try {
            resultCont.first();
            resultConsu.first();
            int ind = resultCont.getInt(colChave);
            String linha[] = new String[ind];
            for(int i = 0; i < ind; i++){
                linha[i] = resultConsu.getString(colunBD);
                resultConsu.next();
            }
            resultConsu.first();
            tabela.addColumn(coluna);
            modelo.addColumn(titulo.toString(), linha);
        }
        catch(SQLException erro){
            JOptionPane.showMessageDialog(null,"Erro ao listar no JTable " + erro);
        }
    }

e aqui o método onde eu estou tendo problemas, metodo remove colunas

    public static void removeColuna(JTable Tabela, String titulo){
        int indice = Tabela.getColumnModel().getColumnIndex(titulo);
        Tabela.removeColumn(Tabela.getColumnModel().getColumn(indice));
    }
}

Bem agora antes que alguém pergunte. NÃO!, eu não estou usando AbtractTableModel, estou usando default.
Agora alguns motivos para eu não usa AbtractTableModel (1- A aplicação já está toda escrita em cima de DefualtTableModel sendo assim fica inviável mudar toda a estrutura das várias JTables existentes na aplicação, 2- Esse é meu primeiro trabalho sério em java não tenho grande dominio sobre JTable, 3- Implementar AbtractTableModel é complexo até hoje nunca achei nada de fácil entendimento sobre AbstractTableModel, 4- Implementar AbstractTableModel exigiria que eu dedicasse um tempo (sabesse-la quanto) para estudar o assunto e no momento eu estou correndo contra o tempo para concluir essa migração de um sistema CLIPPER para JAVA.

Em outro forum eu encontrei um tópico de alguém que estava tendo o mesmo problema, a galera até tentou ajudar até que apareceu um falando de AbtractTableModel e pronto, o tópico morreu.

Então peço aos senhores que por gentileza não me falem de AbstractTableModel :lol:

Eu conheço esse recursso, sei que ele trás muitas vantagens mas no momento ele não está adequado nesse caso.

Agradeço a todos que tentarem ajudar.

O que eu reparei é que tato JTable quanto o DefaultTableModel tem o metodo addColumn() porém o mesmo não acontece com o metodo removeColumn onde só o JTable tem. Então eu axo que pela lógica assim como para eu adicionar uma coluna eu adiciono na JTable e no modelo teria que ser assim também para remover, remover tanto na JTable quanto no modelo.

Alguém tem alguma dica?

Como voce não quer ouvir falar sobre AbstractTableModel a unica coisa que te digo.

Com o Default a unica solução que voce tem é criar um novo e setar na JTable.

good luck

Realmente, vai ter trabalho nesse sentido. Se talvez você tivesse alterado desde o primeiro tópico, teria menos trabalho agora.
Mas agora, terá que mudar tudo, não tem jeito.

Me desculpe. Mas se você usa profissionalmente um exemplo, seu trabalho não é sério. Se deveria ser sério, acho bom você parar um tempo e estudar o uso correto do componente.

Ele é menos complexo do que usar o DefaultTableModel. Mas sim, exige que você dedique um tempo e entenda o conceito envolvido em seu uso. Não estamos sugerindo o seu uso aqui porque queremos que você sofra, ou porque temos tendência sádicas. Estamos sugerindo pois é a forma correta de se fazer. Se você não quer aprender a forma correta, ou como o Java trabalha, sugiro que não migre seu sistema de Clipper para Java.

Mas aqui cheira o real motivo em seus argumentos. Você não quer aprender. Seja porque está encontrando dificuldades, ou porque teria que estudar os conceitos de OO também. E por isso, desistiu e está tentando tomar um caminho que para você “cheira” mais fácil. Mas tenha certeza do seguinte: não é. Você está tomando o caminho menos flexível, menos eficiente e mais difícil.

Acho melhor se perguntar quanto tempo você já perdeu fazendo a implementação de maneira incorreta. Quanto tempo ainda está disposto a sacrificar mantendo o uso desse modelo difícil, ou quanto tempo irá sacrificar para fazer gambiarras para que o DefaultTableModel se comporte de maneiras mais complexas do que a implementação feijão com arroz para qual ele foi criado.

Por isso, pare de jogar tempo fora e faça um esforço genuíno para entender como o JTable funciona.

Exceto a parte do trabalho não ser sério e o da hipotese de eu não querer aprender o restantes das críticas são construtivas.

O trabalho é extremamente sério e não se pode negar que trabalhar com DefaultTableModel é muito mais simples do que implementar abstract, desde que o objetivo da JTable sejá só o fejão com arroz como vc falow, para trabalhos mais complexos e para uma JTable mais dinâmica com certeza AbstractTableModel é bem mais indicado.

Não querer aprender é outra carapuça que não me serve, a linguagem java é extremamente diferente da linguagem clipper com que trabalhei durante anos, muitos amigos meus clippeiros preferiram migrar para linguagem mais amigavel e com sintax mais semelhante ao clipper como Visual FoxPro, XHarbour, FiveWin, VisualObjects e por ai vai. Eu fiz diferente tinha uma visão mais longe queria mais poder e flexibilidade, e quando conheci java não tive dúvidas que seria ela minha nova linguagem. Sou desenvolvedor autônomo nunca fiz um curso de java nem de OO, estou realizando a migração de um de vários sistemas que tenho escrito em clipper para java utilizando apenas os conhecimento adquiridos durante uns anos de estudo autodidata sobre java, o conceito OO eu conheço bem porém só o tempo e a prática fará com que eu consiga tirar 100% do seu proveito.
Realmente essa é uma migração que não tenho muito tempo de prazo para fazer por isso estou pensando em utilizar JTable apenas de forma básica e numa próxima atualização entregar o sistema com uma tabela mais dinâmica. Eu sempre procuro fazer uma implementação da maneira mais correta possivel é por isso que constantemente estou aqui pedindo a opinião da galera mais experiente.

Ainda estou aprocura de informações e exemplos mais claros e limpos de AbstractTableModel, um exemplo voltado para quem tem pouca experiência.
Os tutos que se encontra tem muito código no conteúdo mas pouca explicação do mesmo. Os códigos de exemplo são lançados e apenas uma explicação de modo geral é feita, entender o restante do código fica por conta de quem está lendo. Ai agente sempre se depara com algumas linhas que não foram possiveis entender. Estou a procura de um exemplo mais limpo para profissionais de todos os niveis.

Eu consegui fazer com que a coluna excluida pare de aparecer para isso é necessário apenas uma linha.

Apenas isso resolve o problema, acredito que pelo fato da galera pouco ou nunca usar DefaultTableModel não tem muita familiaridade. Mas ta ai pro caso de alguém ter o mesmo problema.

Porém realmente usar Default dá muita dor de cabeça pois a coluna parou de reaparecer mas agora está gerando uma excessão, vou dar uma analizada e posto o problema ou a solução aqui.

Se alguém poder me indicar mais exemplos do uso de Abstract agradeço, adimito que estou tendo muita dificudade em entender.

Só pra constar. Eu já fui clipeiro e admito que no começo dá muita dificuldade em entender os conceitos de OO e do Java. E, para ser bem sincero, o JTextField com formatação do Java não chega aos pés do existente no clipper.

O que você deve entender é que nos métodos do AbstractTableModel você estará dizendo ao JTable o que ela deve desenhar. Cada método, diz ao java uma coisa diferente.

Por exemplo. Considere que você vai escrever um TableModel para mostrar livros na tela, e quer exibir 2 colunas, uma para o autor (coluna 0) e outra para o título (coluna 1).

public class LivrosTableModel extends AbstractTableModel {
    private static final int COL_AUTOR = 0;
    private static final int COL_TITULO = 1;

    private List&lt;Livro&gt; valores;       

    //Esse é um construtor, que recebe a nossa lista de livros
    public TitulosTableModel(List&lt;Livro&gt; valores) {
          this.valores = new ArrayList&lt;Livro&gt;(valores);
    }

    public int getRowCount() {
        //Quantas linhas tem sua tabela? Uma para cada item da lista.
        return valores.size();
    }

    public int getColumnCount() {
        //Quantas colunas tem a tabela? Nesse exemplo, só 2.
        return 2;
    }

    public String getColumnName(int column) {
        //Qual é o nome das nossas colunas?
        if (column == COL_AUTOR) return "Autor";
        if (column == COL_TITULO) return "Título";
        return ""; //Nunca deve ocorrer
    }

    public Object getValueAt(int row, int column) {
        //Precisamos retornar o valor da coluna column e da linha row.
        Livro titulo = valores.get(row);
        if (column == COL_AUTOR) return titulo.getTitulo();
        else if (column == COL_TITULO) return titulo.getAutor().getNome();
        return ""; //Nunca deve ocorrer
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        Livro titulo = valores.get(row);
        //Vamos alterar o valor da coluna columnIndex na linha rowIndex com o valor aValue passado no parâmetro.
        //Note que vc poderia alterar 2 campos ao invés de um só.
        if (columnIndex== COL_TITULO) titulo.setTitulo(aValue.toString());
        else if (columnIndex== COL_AUTOR) titulo.getAutor().setNome(aValue.toString());
    }

    public Class<?> getColumnClass(int columnIndex) {
        //Qual a classe das nossas colunas? Como estamos exibindo texto, é string.
        return String.class;
    }
    
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        //Indicamos se a célula da rowIndex e da columnIndex é editável. Nossa tabela toda é.
        return true;
    }
    //Já que esse tableModel é de livros, vamos fazer um get que retorne um livro inteiro.
    //Isso elimina a necessidade de chamar o getValueAt() nas telas. 
    public Livro get(int row) {
        return valores.get(row);
    }
}

Depois, para usar esse model vc só faz:
suaTable.setModel(new LivrosTableModel(listaDeLivros));

O que o Java faz na hora de desenhar a tabela? O objeto JTable:

  1. Chama os método getColumnsCount() e getRowsCount() para descobrir quantas linhas e colunas deve desenhar;
  2. Chama o método getColumnTitle() para saber que informações irá no título da tabela;
  3. Chama o método getValueAt para saber o que escrever dentro da célula
  4. Chama o método getColumnClass() para saber como desenhar a informação. Em células do tipo Boolean.class, aparecerá um checkbox, células do tipo Integer.class serão alinhadas a direita.
  5. E, também, chama o método isCellEditable para verificar se deve ou não habilitar o editor.

O que as vezes demora a cair a ficha é notar que seus métodos estão respondendo ao Java. Quem chamará um método como
public Object getValueAt(int row, int column) é o JTable, enquanto está se desenhando.
Ele que dirá “Ei, quero desenhar o objeto da linha 1, coluna 3, que valor você tem pra mim aí?”

Você vai notar, depois que aprender a usar o AbstractTableModel, que o trabalho que você tem hoje no Default é enorme. Parece ainda maior se você considerar a existência de modelos ainda mais automáticos, como o do Mark. O seu código atual também fica muito desorganizado. A escrita da tabela se mistura ao código da interface gráfica. Estendendo do abstract, tudo fica separadinho:
Desenho é com a JTable
Dados é com o model
Lógica da interface é na Tela.

Desculpe o tom provocativo do post anterior. A idéia era realmente te irritar. Nem que vc se motivasse novamente a aprender o model, só pra depois jogar na minha cara. :lol:

O esforço realmente vale a pena.

Só pra constar.

O link do TableModel que fiz está na minha assinatura e na do ViniGodoy.

ObjectTableModel

Oi,

olha o código que encontrei que não precisa a AbstractTable.:

[code] public void removeColumnAndData(JTable table, int vColIndex) {
MyDefaultTableModel model = (MyDefaultTableModel)table.getModel();
TableColumn col = table.getColumnModel().getColumn(vColIndex);
int columnModelIndex = col.getModelIndex();
Vector data = model.getDataVector();
Vector colIds = model.getColumnIdentifiers();

table.removeColumn(col);

colIds.removeElementAt(columnModelIndex);

for (int r=0; r<data.size(); r++) {
  Vector row = (Vector)data.get(r);
  row.removeElementAt(columnModelIndex);
}
model.setDataVector(data, colIds);

Enumeration enume = table.getColumnModel().getColumns();
for (; enume.hasMoreElements(); ) {
  TableColumn c = (TableColumn)enume.nextElement();
  if (c.getModelIndex() >= columnModelIndex) {
    c.setModelIndex(c.getModelIndex()-1);
  }
}
model.fireTableStructureChanged();

}

class MyDefaultTableModel extends DefaultTableModel {
public Vector getColumnIdentifiers() {
return columnIdentifiers;
}
}[/code]

Tchauzin!

Ae ViniGodoy, show de bola cara, até que em fim consegui entender essa bagaça.
Realmente é muito simples, seu exemplo me ajudou até a entender o que é um modelo que sinceramente antes eu não sabia, apenas fazia de forma mecânica o que eu tinha visto nos livros, realmente fica bem mais elegante.
Se não for muito trabalho vc poderia deixar esse tópico fixo, para que outros que tenham dificudade de entendar AbstractTableModel consigam de uma vez entender com esse tópico.
Axo que todos os tutoriais deveriam ser assim usar exemplos simples e explicando todo o processo. E não exemplos complexos com pouca explicação do código.
Se vc concordar em deixar fixo pode mudar o titulo para que fique mais indicativo do conteúdo.
Fiquei tão empolgado que vou tentar convencer o cliente para ganhar mais um tempo e implementar logo abstract em todas as JTables do sistema, até porque no projeto minhas JTables serão extremamente dinâmicas (ex: incluir e excluir colunas em tempo real; clicar no titulo da coluna para definir por onde vai ser a ordenação e se vai ser crescente ou decrescente; emais alguns detalhes…)

Durante a fase de implementação qualquer dúvida eu posto aqui no forúm.

Valeu…

Agora o desafio vai ser criar um modelo que sirva para todas as JTable do sistema, para isso ele deverá se inteligente pois não saberá com antecedência qual a quantidade de colunas nem o nome das mesmas.

Bem ja fiz coisas parecidas com isso em outras classes do sistema, vamo ver o que eu consigo fazer.

O resultado eu posto aqui. Se alguém poder dar uma dica será bem vinda.

falow

Minha dica é essa.

ObjectTableModel

Ela é bem genérica e voce só precisa desse Model para todas JTables.

Aproveita e dá uma olhada também no TableModel de auto-filtro, que está na minha assinatura.

Ele permite o auto-filtro igual ao do excel. Assim, seu cliente poderá tanto ordenar, quando filtrar dados repetidos. O modelo é configurável e permite que você escolha que colunas conterão ordenação, ordenação e filtragem e quais não conterão nada.

Ele pode ser usado em conjunto com qualquer TableModel, inclusive o do Mark, ou os seus próprios.

[quote=ViniGodoy]Só pra constar. Eu já fui clipeiro e admito que no começo dá muita dificuldade em entender os conceitos de OO e do Java. E, para ser bem sincero, o JTextField com formatação do Java não chega aos pés do existente no clipper.

O que você deve entender é que nos métodos do AbstractTableModel você estará dizendo ao JTable o que ela deve desenhar. Cada método, diz ao java uma coisa diferente.

Por exemplo. Considere que você vai escrever um TableModel para mostrar livros na tela, e quer exibir 2 colunas, uma para o autor (coluna 0) e outra para o título (coluna 1).

public class LivrosTableModel extends AbstractTableModel {
    private static final int COL_AUTOR = 0;
    private static final int COL_TITULO = 1;

    private List&lt;Livro&gt; valores;       

    //Esse é um construtor, que recebe a nossa lista de livros
    public TitulosTableModel(List&lt;Livro&gt; valores) {
          this.valores = new ArrayList&lt;Livro&gt;(valores);
    }

    public int getRowCount() {
        //Quantas linhas tem sua tabela? Uma para cada item da lista.
        return valores.size();
    }

    public int getColumnCount() {
        //Quantas colunas tem a tabela? Nesse exemplo, só 2.
        return 2;
    }

    public String getColumnName(int columnIndex) {
        //Qual é o nome das nossas colunas?
        if (column == COL_AUTOR) return "Autor";
        if (column == COL_TITULO) return "Título";
    }

    public Object getValueAt(int row, int column) {
        //Precisamos retornar o valor da coluna column e da linha row.
        Livro titulo = valores.get(row);
        if (column == COL_AUTOR) return titulo.getTitulo();
        else if (column == COL_TITULO) return titulo.getAutor().getNome();
    }

    public  void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        //Vamos alterar o valor da coluna columnIndex na linha rowIndex com o valor aValue passado no parâmetro.
        //Note que vc poderia alterar 2 campos ao invés de um só.
        if (column == COL_TITULO) titulo.setTitulo(aValue.toString());
        else if (column == COL_AUTOR) titulo.getAutor().setNome(aValue.toString());
    }

    public  Class getColumnClass(int columnIndex) {
        //Qual a classe das nossas colunas? Como estamos exibindo texto, é string.
        return String.class;
    }
    
    boolean isCellEditable(int rowIndex, int columnIndex) {
        //Indicamos se a célula da rowIndex e da columnIndex é editável. Nossa tabela toda é.
        return true;
    }
    //Já que esse tableModel é de livros, vamos fazer um get que retorne um livro inteiro.
    //Isso elimina a necessidade de chamar o getValueAt() nas telas. 
    public Livro get(int row) {
        return valores.get(row);
    }
}

Depois, para usar esse model vc só faz:
suaTable.setModel(new LivrosTableModel(listaDeLivros));

O que o Java faz na hora de desenhar a tabela? O objeto JTable:

  1. Chama os método getColumnsCount() e getRowsCount() para descobrir quantas linhas e colunas deve desenhar;
  2. Chama o método getColumnTitle() para saber que informações irá no título da tabela;
  3. Chama o método getValueAt para saber o que escrever dentro da célula
  4. Chama o método getColumnClass() para saber como desenhar a informação. Em células do tipo Boolean.class, aparecerá um checkbox, células do tipo Integer.class serão alinhadas a direita.
  5. E, também, chama o método isCellEditable para verificar se deve ou não habilitar o editor.

O que as vezes demora a cair a ficha é notar que seus métodos estão respondendo ao Java. Quem chamará um método como
public Object getValueAt(int row, int column) é o JTable, enquanto está se desenhando.
Ele que dirá “Ei, quero desenhar o objeto da linha 1, coluna 3, que valor você tem pra mim aí?”

Você vai notar, depois que aprender a usar o AbstractTableModel, que o trabalho que você tem hoje no Default é enorme. Parece ainda maior se você considerar a existência de modelos ainda mais automáticos, como o do Mark. O seu código atual também fica muito desorganizado. A escrita da tabela se mistura ao código da interface gráfica. Estendendo do abstract, tudo fica separadinho:
Desenho é com a JTable
Dados é com o model
Lógica da interface é na Tela.

Desculpe o tom provocativo do post anterior. A idéia era realmente te irritar. Nem que vc se motivasse novamente a aprender o model, só pra depois jogar na minha cara. :lol:

O esforço realmente vale a pena.[/quote]

ViniGodoy utilizando seu exemplo ocorreu alguns erros que não consegui identificar, o que seria o “column” no código ?

valew

É o índice da coluna. Recebido como parâmetro. Em um método estava errado mesmo, pq chamei o parâmetro de columnIndex. Já corrigi lá em cima.

Entendi o parâmetro. Só mais uma coisa Vini, em dois métodos o “getValueAt” e o “setValueAt” está dando erro no cabeçalho do método “missing return statement”, o que poderia ser?

obrigado.

obs: sou novo em java.

Entendi o parâmetro. Só mais uma coisa Vini, em dois métodos o “getValueAt” e o “setValueAt” está dando erro no cabeçalho do método “missing return statement”, o que poderia ser?

obrigado.

obs: sou novo em java.[/quote]
O getValueAt é um método que deve retornar um Object. No caso é feita uma verificação: se a coluna é um, retorna uma coisa; se é dois, retorna outra. Mas se não for um nem dois? Por isso o compilador reclama. Teoricamente isso não deve acontecer e a maneira mais simples de corrigir isso é lançar uma exceção:

public Object getValueAt(int row, int column) {  
        //Precisamos retornar o valor da coluna column e da linha row.  
        Livro titulo = valores.get(row);  
        if (column == COL_AUTOR) return titulo.getTitulo();  
        else if (column == COL_TITULO) return titulo.getAutor().getNome();
        throw new IllegalArgumentException("Invalid column");
}  

O método setValueAt não deveria apresentar esse erro uma vez que ele tem retorno void (vazio).

Entendi o parâmetro. Só mais uma coisa Vini, em dois métodos o “getValueAt” e o “setValueAt” está dando erro no cabeçalho do método “missing return statement”, o que poderia ser?

obrigado.

obs: sou novo em java.[/quote]
O getValueAt é um método que deve retornar um Object. No caso é feita uma verificação: se a coluna é um, retorna uma coisa; se é dois, retorna outra. Mas se não for um nem dois? Por isso o compilador reclama. Teoricamente isso não deve acontecer e a maneira mais simples de corrigir isso é lançar uma exceção:

public Object getValueAt(int row, int column) {  
        //Precisamos retornar o valor da coluna column e da linha row.  
        Livro titulo = valores.get(row);  
        if (column == COL_AUTOR) return titulo.getTitulo();  
        else if (column == COL_TITULO) return titulo.getAutor().getNome();
        throw new IllegalArgumentException("Invalid column");
}  

O método setValueAt não deveria apresentar esse erro uma vez que ele tem retorno void (vazio).[/quote]

Deu certo Marco, na hora que inclui a exceçao no “getValueAt” parou também o erro no “setValueAt”.

Mais uma dúvida eu chamo o método dessa maneira, conforme o exemplo do Vini:

suaTable.setModel(new LivrosTableModel(listaDeLivros));

no caso o “listaDeLivros” é a lista com os dados vindo do banco de dados?

valew

[quote=neto.fiamenghi]
Mais uma dúvida eu chamo o método dessa maneira, conforme o exemplo do Vini:

suaTable.setModel(new LivrosTableModel(listaDeLivros));

no caso o “listaDeLivros” é a lista com os dados vindo do banco de dados?

valew[/quote]
Exatamente. Essa lista deve vir da sua classe DAO após fazer a consulta no banco.

[quote=marcobiscaro2112][quote=neto.fiamenghi]
Mais uma dúvida eu chamo o método dessa maneira, conforme o exemplo do Vini:

suaTable.setModel(new LivrosTableModel(listaDeLivros));

no caso o “listaDeLivros” é a lista com os dados vindo do banco de dados?

valew[/quote]
Exatamente. Essa lista deve vir da sua classe DAO após fazer a consulta no banco.[/quote]

Valew Marco. :smiley: ficou show.

Estou estudando o artigo acima e gostaria de saber, nessa linha:

private List<Livro> valores; 

esse é uma classe?? de onde surgiu??