Fórum JTable + DefaultTableModel + Imagem em Cell #401777
25/05/2011
0
Gostaria de poder colocar uma imagem dentro de uma determinada célula de um JTable. Eu determinaria a mesma pela posição na linha e coluna e adicionaria uma imagem. Acredito que a mesma seria colocada dentro de um JLabel.
A minha idéia é fazer, por exemplo:
__________________________________
Gerente | V
__________________________________
Supervisor | X
__________________________________
Sendo que a segunda coluna teria na célula[1,2] uma imagem de V e na célula[2,2] uma imagem de um X. Este é apenas um exemplo simples.
Já tenho implementado o seguinte:
class JComponentTableCellRendererP implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return (JComponent) value;
}
}
public class Principal extends javax.swing.JFrame {
(...)
//----------------------------------------------------------------------------//
private String headers_registro[] = { "Evento", "Status" };
private final Object rows_registro[][] = { { " ", " " }, { " ", " " }, { " ", " " }};
public DefaultTableCellRenderer renderer_registro = null;
private Border headerBorder_registro = null;
private TableCellRenderer cellrenderer_registro = new JComponentTableCellRendererP();
public TableColumnModel columnModel_registro = null;
private TableColumn column0_registro = null;
private TableColumn column1_registro = null;
private JLabel evento_registroLabel = null;
private JLabel status_registroLabel = null;
private DefaultTableModel modelo_registro = new DefaultTableModel(rows_registro, headers_registro){
@Override
public boolean isCellEditable(int row, int col){
return false;
}
};
//----------------------------------------------------------------------------//
(...)
private void popularTabelaRegistro(){
String[] dados = null;
dados = new String[2];
//-------------- ELIMINANDO TOTALMENTE TODAS AS LINHAS DA TABELA -------------//
modelo_registro.getDataVector().removeAllElements();
//----------------------------------------------------------------------------//
dados[0] = "Programa Inicializado"; // Evento
dados[1] = dt.obterHora() + " - " + dt.obterData(); // Status
modelo_registro.addRow(dados);
//----------------------- CENTRALIZANDO OS DADOS NO JTABLE -------------------//
renderer_registro.setHorizontalAlignment(JLabel.CENTER);
//----------------------------------------------------------------------------//
(...)
Gostaria de apartir do que eu já tenho fazer a referida implementação das imagens no JTable. Desde já agradeço qualquer ajuda neste sentido.
Abraão
Abraão Monteiro
Curtir tópico
+ 0Posts
26/05/2011
Anthony Accioly
O approach tradicional para o que você está tentando fazer é escrever um TableCellRenderer (como você começou a implementar) e associá-lo à coluna através do método setCellRenderer.
Eis um exemplo:
http://www.java2s.com/Code/Java/Swing-JFC/RenderinganimageinaJTablecollumn.htm
Veja que você pode fazer um if em cima do parâmetro value para determinar qual imagem usar.
Um outro approach "espertinho" é usar html com os componentes swing e mandar ver no uso da tag <img>: http://forum.codecall.net/java-tutorials/4418-html-swing-components.html
Qualquer coisa estamos aí.
Gostei + 0
30/05/2011
Abraão Monteiro
Baseado no link eu implementei algo assim:
class RenderizaCelula extends DefaultTableCellRenderer {
// Diretório do JRE
private File diretorio1 = new File("C:\\Arquivos de programas\\Java");
// Diretório de Gerencimento do Banco de Dados (SQLyog Enterprise)
private File diretorio2 = new File("C:\\Arquivos de programas\\SQLyog Enterprise");
// Diretório da localização física do Banco de Dados
private File diretorio3 = new File("C:\\Arquivos de programas\\MySQL\\MySQL Server 5.0\\data\\sagitarius");
// Diretório da localização física dos arquivos do Programa
private File diretorio4 = new File("C:\\Sagitarius");
// Diretório da localização física do log do Programa
private File diretorio5 = new File("C:\\Sagitarius\\LOG_GUI");
// Diretório da localização física do backup do banco de dados
private File diretorio6 = new File("C:\\Sagitarius\\Backup");
public RenderizaCelula(){
// TRABALHAR OS PARÂMETROS DE ENTRADA E MELHORAR O METODO ABAIXO
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,boolean hasFocus, int row, int column) {
if ((row==1)&&(column==1)&&(table==Principal.tabela_registro_sistema))
return(preencherCelula("C:\\Sagitarius\\usuario.PNG"," [ GERENTE ]"));
else if ((row==2)&&(column==1)&&(table==Principal.tabela_registro_sistema))
return(preencherCelula("C:\\Sagitarius\\checagem_inicial.PNG",""));
else if ((row==3)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio1.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}else if ((row==4)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio2.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}else if ((row==5)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio3.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}else if ((row==6)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio4.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}else if ((row==7)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio5.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}else if ((row==8)&&(column==1)&&(table==Principal.tabela_registro_sistema)){
if(diretorio6.exists())
return(preencherCelula("C:\\Sagitarius\\setando_certo.PNG",""));
else
return(preencherCelula("C:\\Sagitarius\\setando_errado.PNG",""));
}
else
return(preencherCelula("",(String) value));
}
private Component preencherCelula(String caminho_imagem, String texto){
JLabel imagem_label = new JLabel();
ImageIcon imagem = null;
imagem_label.setText(texto);
imagem_label.setHorizontalAlignment(JLabel.CENTER);
imagem_label.setVerticalAlignment(JLabel.CENTER);
if (!caminho_imagem.isEmpty()){
imagem = new ImageIcon(caminho_imagem);
imagem_label.setIcon(imagem);
}
return(imagem_label);
}
// The following methods override the defaults for performance reasons
// + Fonte: http://www.exampledepot.com/egs/javax.swing.table/CustRend.html
@Override
public void validate() {}
@Override
public void revalidate() {}
@Override
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {}
@Override
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
}
//----------------------------------------------------------------------------//
Eu gostaria de ao longo do projeto ir adicionando tipo o número da linha, a coluna, o caminho da imagem e o texto da célula e em seguida mandar renderizar a mesma. Eu teria que colocar estes quatro parâmetros em uma matriz que fosse dinamicamente expandida na vertical e em seguida chamar novamente a classe? Estou chamando a classe acima uma única vez da seguinte forma:
// Renderiza as celulas do corpo da JTable column1_registro.setCellRenderer(new RenderizaCelula()); column0_registro.setCellRenderer(new RenderizaCelula());
Teria alguma idéia ou opinião. Desde já agradeço!
Gostei + 0
31/05/2011
Anthony Accioly
Eu eliminaria todos esses ifs posicionais e leria o caminho da imagem direto do valor da célula.
Exemplo:
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
String caminho = (String) value;
JLabel lbl = new JLabel();
ImageIcon icon = new ImageIcon(caminho);
lbl.setHorizontalAlignment(JLabel.CENTER);
lbl.setVerticalAlignment(JLabel.CENTER);
lbl.setIcon(icon);
return lbl;
}
Ou ainda, como minha sugestão inicial. Escolher a imagem dinamicamente a partir do valor. Vamos dizer por exemplo que sua célula contivesse um boolean. true exibe a imagem para certo e false exibe a imagem para errado.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Boolean correto = (Boolean) value;
JLabel lbl = new JLabel();
ImageIcon icon = null;
if (corrreto) {
icon = new ImageIcon("C:\\Sagitarius\\setando_certo.PNG");
} else {
icon = new ImageIcon("C:\\Sagitarius\\setando_errado.PNG");
}
lbl.setHorizontalAlignment(JLabel.CENTER);
lbl.setVerticalAlignment(JLabel.CENTER);
lbl.setIcon(icon);
return lbl;
}
Código genérico é sempre preferível a código posicional / caso a caso. Desse jeito você só muda o valor do campo na coleção / matriz / TableModel associado à tabela.
Finalmente, eu sou contra chumbar caminhos absolutos para imagens. Prefiro colocar elas no Classpath e ler através do método getResource. Por exemplo, coloque as imagens no projeto dentro do diretório imagens. Daí abra como no exemplo.
ImageIcon icon = new ImageIcon(getClass().getResource("/imagens/setando_certo.PNG"));Por que isso? Imagine que seu software vai rodar em Linux, ou mesmo em um computador Windows cujo HD está mapeado em D:. É sempre melhor gerar um arquivo jar auto-contido com caminhos relativos do que depender de recursos externos.
Abraços,
Gostei + 0
31/05/2011
Abraão Monteiro
RenderizaCelula(linha,coluna,caminho_imagem,texto); (1)
Sendo:
linha -> Row do JTable
coluna -> Column do JTable
caminho_imagem -> Seria o Classpath com a imagem específica e variada para cada linha
texto -> O texto que estaria ao lado da imagem dentro da célula
E este seria setado na renderização do JTable. Não sei se monto uma matriz de string com cada parâmetro e passo para o construtor da classe RenderizaCelula. Tipo:
RenderizaCelula(String dados_registro[][][][]); // Acredito que ficaria um pouco estranho para escrever
Como informação complementar, eu escrevo na JTable utilizando:
private DefaultTableModel modelo_registro = new DefaultTableModel(rows_registro, headers_registro){
@Override
public boolean isCellEditable(int row, int col){
return false;
}
};
public String[] dados = new String[2];
dados[0] = "Programa Inicializado"; // Evento
dados[1] = dt.obterHora() + " - " + dt.obterData(); // Status
modelo_registro.addRow(dados);
Ao longo do programa alguns eventos são disparados e com o objeto modelo_registro eu acrescento novas linhas. Com a parametrização acima (1) eu gostaria de poder ao adicionar uma nova linha ter a opção de renderizar a célula com uma imagem ou não, dependendo da situação. Sinceramente eu estou um pouco perdido, não sei se cheguei a esclarecer e passar a idéia correta para você. Qualquer coisa, na medida do possível, posta novamente que eu tento explicar melhor. Agradeço a atenção.
Gostei + 0
01/06/2011
Anthony Accioly
De qualquer forma, eu não usaria uma matriz para isso.
Se você está tomando o tempo para escrever um TableModel, por que não usar, por exemplo, uma lista de objetos complexos? Cada entrada na lista é uma linha, e cada atributo do objeto uma coluna.
Por exemplo:
public class FuncionarioVO {
private Integer id;
private String nome;
private ImagemVO imagem;
//... Getters e Setters
}
public class ImagemVO {
private String caminho;
private String descricao;
//... Getters e Setters
}
E você alimenta seu TableModel com uma
List<FuncionarioVO> dados;
Dessa forma você consegue aquela solução genérica apontada acima para renderizar os valores da coleção. Para fazer uma linha nova aparecer na tabela, basta que você adicione um item novo na coleção entendeu?
Eu estou insistindo para você que essa abordagem posicional não é indicada. O fato de você precisar de uma matriz de quatro dimensões reflete isso.
Se você precisa tanto de texto quanto da descrição na mesma coluna, então pode fazer uma lógica em cima do objeto ImagemVO.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
ImagemVO imagemVO = (ImagemVO) value;
JLabel lbl = new JLabel();
if (imagemVO.getCaminho() != null) {
ImageIcon icon = new ImageIcon(imagemVO.getCaminho());
lbl.setIcon(icon);
}
if (imagemVO.getDescricao() != null) {
lbl.setText(imagemVO.getDescricao());
}
lbl.setHorizontalAlignment(JLabel.CENTER);
lbl.setVerticalAlignment(JLabel.CENTER);
return lbl;
}
Seu método para inserir uma linha na tabela seria algo assim:
void renderizaCelula(FuncionarioVO funcionario) {
dados.add(funcionario);
}
Você também pode atualizar os dados da lista e indicar para a tabela que ela deve ser atualizada:
tableModel.fireTableDataChanged();
Outra coisa, você está sofrendo bastante para reinventar a roda de uma boa solução. Já que o problema deixou de ser trivial, eu recomendo fortemente o uso de uma biblioteca como:
http://www.glazedlists.com
http://kenai.com/projects/betterbeansbinding/pages/Home
http://java.net/projects/binding/
Com essas bibliotecas você não vai precisar se preocupar em escrever o TableModel, apenas configurar o binding entre a lista e a tabela. As bibliotecas também se encarregam de manter os dados da tabela sincronizados com a lista e vice-versa.
Abraços,
Gostei + 0
01/06/2011
Abraão Monteiro
ImagemVO imagemVO = (ImagemVO) value;
Estarei este final de semana colocando em prática todas estas idéias. Já dei uma olhada também na Glazed List e achei bem interessante, posteriormente vou ler com bastante calma a documentação. Agradeço a atenção.
Gostei + 0
02/06/2011
Anthony Accioly
ImagemVO imagemVO = (ImagemVO) value;
Opa,
Vamos lá: Cada componente associado ao JTable tem sua responsabilidade. O TableModel, a partir do método getValueAt, diz quais dados devem ser disponibilizados para a tabela. Com esse método você pode dizer, por exemplo, que cada linha deve ser associada a um elemento de uma lista, e que, por exemplo, a coluna 3 deve receber o campo imagemVO do elemento.
No código que foi postado você estava usando exclusivamente Strings. Você não precisa precisa se limitar a elas, podendo associar o tipo que lhe for mais conveniente (no caso, ImagemVO).
Como você pode imaginar o TableCellRenderer é um componente com a responsabilidade de controlar como exibir os valores passados pelo TableModel. O método getTableCellRendererComponent recebe várias informações incluindo o valor passado pela TableModel e suas posições.
No seu código, você está usando o renderer e as informações de linha e coluna para determinar qual imagem deverá ser exibida... Veja que isso fere a divisão de responsabilidade dos componentes... O TableCellRenderer não deve ditar o que será exibido, apenas o "como será exibido".
Pois bem, se estamos trabalhando com um TableCellRenderer específico para uma coluna e sabemos que o TableModel estará voltando um objeto do tipo ImagemVO para ela (caso do exemplo acima), então é seguro fazer casting do valor da célula para o tipo ImagemVO.
Sanei a dúvida?
Gostei + 0
03/06/2011
Anthony Accioly
Respondido?
Se sim, por favor feche o chamado.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)