JTable + JComboBox

Boa tarde.

Estou à vários dias com um problema, ao tentar fazer o seguinte (espero conseguir explicar-me):

Tenho uma JTable, com três colunas (a primeira oculta), em que uma das colunas deve ter uma JComboBox.

A primeira coluna contém um ID (oculta), a segunda contém uma instância de uma classe, e a terceira um campo dessa instância.

Tenho visto pela Internet (e aqui pelo GUJ) vários problemas parecidos com o meu, mas não arranjo solução!

O que eu pretendo fazer é preencher a JComboBox com valores da base de dados, e o utilizador poder escolher um desses valores na coluna de “Título do Relatório”.
Quando o utilizador escolher um desses valores, a primeira coluna (ID) e a última coluna (Periodicidade) ficam com os valores daquele relatório automaticamente. (através do objecto acedo aos campos ‘ID’ e ‘Periodicidade’)

Neste momento não consigo:

  • fazer com que a JComboBox reconheça o valor que tem e coloque automaticamente o focus nesse valor (algo como ‘setSelectedItem()’);

  • ao mudar de uma linha para a outra, os valores na coluna começam a repetir-se, ou seja, fica com o valor que eu coloquei na última linha.
    Por exemplo, se eu na linha 3 seleccionar ‘teste2’ da JComboBox, e depois fizer um clique na linha 1, ela vai ficar com ‘teste2’ automaticamente, em vez de manter o ‘Relatório de coisas’.

Não sei se o melhor é utilizar um TableModel meu (modificado), com um CellEditor também meu, para poder fazer isto. Mas como o problema se mantém há vários dias, tive mesmo que abrir um tópico.

Espero que me possam ajudar, qualquer dúvida (código, ou melhor explicação) posso dar!

Cumprimentos,

Já consegui alguns progressos! :slight_smile:

Estou a utilizar um TableModel ‘criado’ por mim (penso que é baseado num que vi aqui pelo GUJ).

[code]public class MyTableModel extends AbstractTableModel {

private String[] columnNames = {};
private ArrayList<Object[]> dataList = new ArrayList<Object[]>();

(...)

[/code]

Depois implementei todos os métodos (fiz o override de alguns, e criei outros).

Tenho também este CellEditor:

    class MyCellEditor extends AbstractCellEditor implements TableCellEditor {

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            ArrayList<Relatorio> listaRelatorios = null;
            comboBoxRelatorios = new JComboBox();
            try {
                listaRelatorios = bllRelatorios.getRelatorios();

                for (int i = 0; i < listaRelatorios.size(); i++) {
                    comboBoxRelatorios.addItem(listaRelatorios.get(i).TOSTRING());
                }
                comboBoxRelatorios.setSelectedItem(value.TOSTRING()); //Deixa o editor pré-selecionado com o valor da célula

                comboBoxRelatorios.addItemListener(new java.awt.event.ItemListener() {
                    @Override
                    public void itemStateChanged(ItemEvent e) {
                        //comboBoxRelatoriosStateChanged(e, tableRelatoriosAspectos.getSelectedRow());
                    }
                });
                
                return comboBoxRelatorios;
            } catch (SQLException ex) { return null;}
        }

        @Override
        public Object getCellEditorValue() {
            return comboBoxRelatorios.getSelectedItem().TOSTRING();
        }
    }

Mas como se pode ver em cima, o setSelectedItem() só funciona se eu colocar em todos os métodos o toString (em maiúsculas no código).

O meu objectivo é colocar objectos do tipo ‘Relatorio’ dentro do JComboBox. Mas quero que o método setSelectedItem() continue a actuar, detectando automaticamente onde colocar o focus do JComboBox.

Alguma dica, fico agradecido. :slight_smile:

Consegui resolver o meu problema! :slight_smile:

Para alguém que possa passar pelo mesmo, fica a solução:

  • Utilizei uma JTable nativa do JAVA;
  • Criei um TableModel (MyTableModel), fazendo o override de alguns métodos, e criando outros que me davam jeito;
  • Criei um CellEditor (MyCellEditor), onde fiz o override dos métodos getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) e getCellEditorValue(). Este CellEditor foi feito para o editor ser uma JComboBox;
  • Para o método comboBox.setSelectedItem(value) funcionar, tive que fazer o override dos métodos equals(Object obj) e hashcode() da classe que criei.

Segue o código:

Criar TableModel, JTable e popular JTable

MyTableModel tableModelRelatoriosAspectos = new MyTableModel(new String[]{"idRelatorio", "Título do Relatório", "Periodicidade"});
tableRelatoriosAspectos = new javax.swing.JTable();

(...)

XTableColumnModel columnModelRelatoriosAspectos = new XTableColumnModel();
tableRelatoriosAspectos.setColumnModel(columnModelRelatoriosAspectos);
tableRelatoriosAspectos.setModel(tableModelRelatoriosAspectos);
tableRelatoriosAspectos.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

//Ocultar primeira coluna (ID) do JTable
columnModelRelatoriosAspectos.setColumnVisible(tableRelatoriosAspectos.getColumnModel().getColumn(0), false);
columnModelRelatoriosAspectos.getColumn(1).setPreferredWidth(200);
columnModelRelatoriosAspectos.getColumn(1).setMaxWidth(250);
jScrollPane6.setViewportView(tableRelatoriosAspectos);


for (int i = 0; i < aspecto.getListaRelatorios().size(); i++) {
      Object[] dados = {
                        ""+aspecto.getListaRelatorios().get(i).getIdRelatorio(),
                        aspecto.getListaRelatorios().get(i),
                        aspecto.getListaRelatorios().get(i).getPeriodicidadeRelatorios()
      };
      tableModelRelatoriosAspectos.addRow(dados);
}
//Adiciona linha em branco para o utilizador poder inserir novos relatórios
tableModelRelatoriosAspectos.addRow(new Object[]{"", new Relatorio(), ""});

(...)

//Colocar o editor por defeito na da JTable como o MyCellEditor
tableRelatoriosAspectos.setDefaultEditor(Relatorio.class, new MyCellEditor(bllRelatorios.getRelatorios()));

TableModel

import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
import portugen.ambiente.dominio.Relatorio;

public class MyTableModel extends AbstractTableModel {

    private String[] columnNames = {};
    private ArrayList<Object[]> dataList = new ArrayList<Object[]>();

    public MyTableModel(String[] columnNames) {
        this.columnNames = columnNames;
    }
    
    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

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

    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }

    @Override
    public Object getValueAt(int row, int col) {
        // Obtem a linha, que é uma String []
        Object[] linha = dataList.get(row);
        // Retorna o objeto que esta na coluna
        return linha[col]; 
    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     */
    @Override
    public Class getColumnClass(int c) {
        if (c == 1) {
            return Relatorio.class;
        }
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        return (col == 1) ? true : false;
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    @Override
    public void setValueAt(Object value, int row, int col) {
        // Obtem a linha, que é uma String []  
        Object[] linha = dataList.get(row);  
        // Altera o conteudo no indice da coluna passado
        switch (col) {
            case 0:
                linha[col] = value;
                break;
            case 1:
                linha[col] = value;
                break;
            case 2:
                linha[col] = value;
                break;
        }
        // dispara o evento de celula alterada
        fireTableDataChanged();
    }
    
    public void addRow( Object [] dadosLinha){  
        dataList.add(dadosLinha);  
        // Informa a jtable de que houve linhas incluidas no modelo  
        // Como adicionamos no final, pegamos o tamanho total do modelo  
        // menos 1 para obter a linha incluida.  
        int linha = dataList.size()-1;  
        fireTableRowsInserted(linha,linha);  
        return;
    }
    
    public void removeRow(int row){  
        dataList.remove(row);
        // informa a jtable que houve dados deletados passando a   
        // linha removida  
        fireTableRowsDeleted(row,row);  
    }
}

MyCellEditor

import java.awt.Component;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import portugen.ambiente.dominio.Relatorio;

/**
 *
 * @author Pedro Duarte
 */
public class MyCellEditor extends AbstractCellEditor implements TableCellEditor {

    private ArrayList<Relatorio> listaRelatorios = null;
    private JComboBox field = new JComboBox();
    
    public MyCellEditor(ArrayList<Relatorio> array) {
        listaRelatorios = array;
        for (int i = 0; i < listaRelatorios.size(); i++) {
            field.addItem(listaRelatorios.get(i));
        }

    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        field.setSelectedItem(value); //Deixa o editor pré-selecionado com o valor da célula

        return field;
    }

    @Override
    public Object getCellEditorValue() {
        return field.getSelectedItem();
    }
}

Override dos métodos equals e hashcode da minha classe

public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if((obj == null) || (obj.getClass() != this.getClass()))
            return false;
        // object must be Relatorio at this point
        Relatorio rel = (Relatorio)obj;
        if (rel.getIdRelatorio() == this.getIdRelatorio())
            return true;
        return false;
    }
    
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + idRelatorio;
        hash = 31 * hash + (null == tituloRelatorio? 0 : tituloRelatorio.hashCode());
        hash = 31 * hash + (null == descricaoRelatorio? 0 : descricaoRelatorio.hashCode());
        hash = 31 * hash + (null == comentarios? 0 : comentarios.hashCode());
        hash = 31 * hash + (null == notasSobreExecucao? 0 : notasSobreExecucao.hashCode());
        hash = 31 * hash + (null == responsabilidadeExecucao? 0 : responsabilidadeExecucao.hashCode());
        hash = 31 * hash + (null == periodicidadeRelatorios? 0 : periodicidadeRelatorios.hashCode());
        hash = 31 * hash + (null == dataProximoRelatorio? 0 : dataProximoRelatorio.hashCode());
        hash = 31 * hash + (null == destinatarioRelatorio? 0 : destinatarioRelatorio.hashCode());
        hash = 31 * hash + (null == responsabilidadeRelatorio? 0 : responsabilidadeRelatorio.hashCode());
        hash = 31 * hash + (null == dataActualizacaoRelatorio? 0 : dataActualizacaoRelatorio.hashCode());
        hash = 31 * hash + (null == localizacaoRelatorio? 0 : localizacaoRelatorio.hashCode());
        
        
        return hash;
    }

Uma questão apenas:

qual a diferença entre

e

Não é mais produtivo seleccionar o CellEditor apenas para a coluna?