jTable - Matriz de Cruzamento

Pessoal, boa tarde.

Estou iniciando no desenvolvimento Swing, tive maior experiencia com desenvolvimento web.
Estou precisando criar uma matriz de cruzamento utilizando o jTable.

A tabela seria mais ou menos assim:

| --------- | EF1 | EF 2 | EF 3

Requisito 1 | X | | X
Requisito 2 | X | X |
Requisito 3 | | X | X

Ou seja, a quantidade de colunas e linhas são dinâmicas. O objetivo dessa tabela é mostrar os mesmos requisitos que que estão em diferentes especificações funcionais.
Como no exemplo acima, o Requisito 1 está nas EF1 e EF3.

Andei pesquisando e acho que terei que criar um table model, a questão é, como fazer colunas dinamicas?

obrigado desde ja.
abss

Opa, ssh, beleza?

É isso mesmo, você terá que fazer um TableModel próprio que gerencie isso, vai ser bem mais fácil para você.

Veja bem. Quando você cria seu TableModel, você normalmente passa sua coleção de dados pelo construtor. Você pode passar suas colunas também. O que acha?

[code]public class SeuTableModel extends AbstractTableModel {

private List<Classe> lista;
private String[] colunas;

public SeuTableModel(List<Classe> lista, String[] colunas) {
    setLista(lista);
    setColunas(colunas);
}

@Override public String getColumnClass(int column) {
    return String.class;
}

@Override public String getColumnName(int column) {
    return getColunas()[column];
}

@Override Object getValueAt(int row, int column) {

    String coluna = getColunas()[column];
    Classe item = getLista().get(row);

    if (item.getRequisito().equals(coluna)) {
        return "X";
    } else {
        return "";
    }
    return "";
}

}[/code]
Algo parecido com isso. O que acha?

Hmm, muito legal Nicolas.
Estou comelando a entender.

O tableModel pode receber as colunas como um List também né?

E eu também não entendi o preenchemento.

Outra observação é que eu queria que o nome das colunas aparecessem (header) assim como a primeira linha vertical fosse um header.

se puder continuar me ajudando vai me quebrar um galho enorme.

saudades do html. =S

abs

[quote=ssh]Hmm, muito legal Nicolas.
Estou comelando a entender.

O tableModel pode receber as colunas como um List também né?

E eu também não entendi o preenchemento.

Outra observação é que eu queria que o nome das colunas aparecessem (header) assim como a primeira linha vertical fosse um header.

se puder continuar me ajudando vai me quebrar um galho enorme.

saudades do html. =S

abs
[/quote]
Haha, não é tão complicado não. No começo, é chatinho de entender, mas depois criar seus TableModels fica algo trivial.
Sim, você trabalha com a enumeração que você quiser, foi só um exemplo.

O preenchimento funciona assim: você usa o método getValueAt. Esse método diz: “Parceiro, me devolve um valor para colocar na linha X e na coluna Y?”. Daí você vai decidir o que vai devolver. O que eu fiz no exemplo? Eu peguei um objeto da sua coleção de dados na posição X (linha). Daí eu verifiquei se, na coluna Y, com o nome Requisito funcional?, o objeto tem esse valor. Se tem, devolve um “X”; se não, devolve uma string vazia. Foi só um exemplo. Lógico que essa lógica ficará mais elaborada.

Para você exibir o header da JTable, deve-se adicioná-la a um JScrollPane.

JTable tabela = new JTable(new SeuTableModel(lista, colunas)); JScrollPane scroll = new JScrollPane(tabela); getContentPane().add(scroll);

nao tem nem como agradecer a suas dicas…

mais uma duvida, como eu faço p/ primeira coluna ser um header? compreende?

No meu exemplo: A Especificação Funcional Original (é de onde eu começo tudo), dai eu listo ela na primeira coluna e embaixo todos os seus requisitos.

depois eu procuro requisito por requisito pra ver se ele está em mais alguma outra Especificação Funcional, se ele estiver em outra, eu crio uma nova coluna e na linha do requisito eu marco um X.

estou muito perdido. =S

[quote=ssh]nao tem nem como agradecer a suas dicas…

mais uma duvida, como eu faço p/ primeira coluna ser um header? compreende?

No meu exemplo: A Especificação Funcional Original (é de onde eu começo tudo), dai eu listo ela na primeira coluna e embaixo todos os seus requisitos.

depois eu procuro requisito por requisito pra ver se ele está em mais alguma outra Especificação Funcional, se ele estiver em outra, eu crio uma nova coluna e na linha do requisito eu marco um X.

estou muito perdido. =S[/quote]
Compreendo, em partes rs.
Vamos lá: defina “a primeira coluna ser um header”. Você quer que os nomes dos requisitos sejam as colunas, algo como isso? REQUISITO 1 | REQUISITO 2 | REQUISITO 3 | REQUISITO 4 |O resultado final tem que ser esse?

REQUISITO 1 | REQUISITO 2 | REQUISITO 3 | REQUISITO 4 | Item 1 x x x Item 2 x x x Item 3 x x Item 4 x x Se sim, qual a sua modelagem de dados?

Sim, o resultado final tem que ser esse.
OBS: “A primeira coluna ser um header” é exatamente o que você fez com o Item1, Item2, Item3 e Item4

Não existe uma modelagem, estou utilizando um SDK de uma ferramenta para customiza-la. Provavelmente deve existir mas eu desconheço. Mas a relação é de M-N.

1 Especificação Funcional Pode ter vários Requisitos, e 1 Requisito pode estar em várias Especificações Funcionais.

desculpa te amolar muito… =S

abs

Amolar de onde? Estamos aqui no GUJ para sanar nossas dúvidas e ajudar a quem também tem, ué. É sempre um prazer colaborar. :smiley:

Sim, agora entendi o que se passa.
Faça o seu modelo de dados da seguinte maneira:[code]public class Especificacao {

private long ID;
private String nome;
private List<Requisito> requisitos;

public Especificacao() {
    this.requisitos = new ArrayList<Requisito>();
}

public Especificacao(long ID, String nome) {
    this();
    setID(ID);
    setNome(nome);
}

public void addRequisito(Requisito requisito) {
    getRequisitos().add(requisito);
}

}

public class Requisito {

private long ID;
private String nome;

public Requisito() { }
public Requisito(String nome) {
    setNome(nome);
}
public Requisito(long ID, String nome) {
    this(nome);
    setID(ID);
}

}[/code]Quando for buscar os dados, crie uma coleção de especificações e preencha tanto os dados básicos quanto os requisitos destas.[code]Requisito requisitoUm = new Requisito(“Requisito um”);
Requisito requisitoDois = new Requisito(“Requisito dois”);
Requisito requisitoTres = new Requisito(“Requisito três”);
List especificacoes = new ArrayList();

Especificacao especificacaoUm = new Especificacao();
especificacaoUm.setID(1);
especificacaoUm.setNome(“Especificação nº 1”);
especificacaoUm.addRequisito(requisitoUm);
especificacaoUm.addRequisito(requisitoDois);
especificacaoUm.addRequisito(requisitoTres);
especificacoes.add(especificacaoUm);

Especificacao especificacaoDois = new Especificacao();
especificacaoDois.setID(2);
especificacaoDois.setNome(“Especificação nº 2”);
especificacaoDois.addRequisito(requisitoUm);
especificacaoDois.addRequisito(requisitoTres);
especificacoes.add(especificacaoDois);[/code]Agora você pode pegar todos os requisitos e adicionar em um HashSet, eliminando as repetições. É como se você fizesse um distinct() em um banco de dados.[code]
Set requisitosSet = new LinkedHashSet();

        requisitosSet.add(new Requisito(""));
        for (Requisito requisitoEspecificacaoUm : especificacaoUm.getRequisitos()) {
            requisitosSet.add(requisitoEspecificacaoUm);
        }

        for (Requisito requisitoEspecificacaoDois : especificacaoDois.getRequisitos()) {
            requisitosSet.add(requisitoEspecificacaoDois);
        }[/code]Com isso, temos os requisitos, sem repetição, de nossas especificações. Agora, podemos criar um modelo de tabela assim:[code]

public class EspecificacoesTableModel extends AbstractTableModel {

private List<Especificacao> especificacoes = new ArrayList<Especificacao>();
private List<Requisito> requisitos = new ArrayList<Requisito>();

public List<Especificacao> getEspecificacoes() {
    return especificacoes;
}

protected void setEspecificacoes(List<Especificacao> especificacoes) {
    this.especificacoes = especificacoes;
}

public List<Requisito> getRequisitos() {
    return requisitos;
}

protected void setRequisitos(List<Requisito> requisitos) {
    this.requisitos = requisitos;
}

// Recebo, pelo construtor, as especificações e os requisitos que serão as colunas.
public EspecificacoesTableModel(List<Especificacao> especificacoes, List<Requisito> requisitos) {
    setEspecificacoes(especificacoes);
    setRequisitos(requisitos);
}

public int getRowCount() {
    return getEspecificacoes().size();
}

public int getColumnCount() {
    return getRequisitos().size();
}

// Eu pego a especificação atual (cada item da coleção é uma linha).
// Se a coluna atual for 0, quer dizer que vou exibir o nome da especificação.
// Se não, eu vou ver qual o requisito atual, ou seja, o requisito que está sendo exibido na coluna.
// Caso esse requisito que está sendo exibido se encontrar na coleção de requisitos da especificação, eu marco com um X.
// Caso contrário, eu não preencho.
public Object getValueAt(int row, int column) {

    Especificacao especificacao = getEspecificacoes().get(row);
    if (column == 0) {
        return especificacao.getNome();
    }
    else if (column < (getRequisitos().size() + 1)) {
        Requisito requisitoAtual = getRequisitos().get(column);
        return especificacao.getRequisitos().contains(requisitoAtual) ? "X" : "";
    }
}

// Este método é para escrever o nome da coluna. Mesmo esquema de cima: se for a coluna 0, eu escrevo "Especificação".
// Caso contrário, eu coloco qual o nome do requisito.
@Override public String getColumnName(int column) {

    if (column == 0) {
        return "Especificação";
    }
    else {
        return getRequisitos().get(column).getNome();
    }
}

}[/code]Pronto! Agora, simplesmente, é só você chamar a sua tabela na tela. (continuação dos códigos 2 e 3)[code]
EspecificacoesTableModel model = new EspecificacoesTableModel(
especificacoes, new ArrayList(requisitosSet));

        JTable tabela = new JTable(model);
        JScrollPane scroll = new JScrollPane(tabela);
        getContentPane().add(scroll);[/code]

Aqui funcionou perfeitamente. Você pode adicionar mais requisitos, ir trocando os requisitos de cada especificação para você ver o resultado.
É isso que você precisa?

Nicolas, muito obrigado… se ai funcionou aqui provavelmente vai funcionar… só que vou demorar algumas horas p/ conseguir testar.

em breve te mando uma MP falando se deu certo ou não.

abraços muito obrigado mais uma vez.

Edit: Deu certo nicolas, sem palavras, nunca que chegaria nisso sozinho.

agora só preciso pegar as especificações direitinho.
abss