Colorir celula da JTable pelo JButton

Testando uma classe, eu não consigo fazer a célula da JTable ficar com cores diferentes!Eu quero digitar 40 e deixar azul, e ao digitar 50, deixar vermelho(após pressionar o JButton).Mudei um bando de coisa, mas hora fica a linha toda vermelha, ou a linha toda azul.
Todo o código para quem quiser testar:

[code]
import java.awt.;
import java.awt.event.
;
import javax.swing.;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.util.
;

public class JTableDemo extends JDialog implements Runnable {
JTable table = new JTable();
public static void main(String… arg) {
SwingUtilities.invokeLater(new JTableDemo());
}
private final MyColorModel tableModel;

private JTableDemo() {
    setModal(false);
    setTitle("JTable demo");
    setLayout(new BorderLayout(10, 10));

    JToolBar bar = new JToolBar();
    bar.setFloatable(false);
    bar.setRollover(true);

    ButtonGroup g = new ButtonGroup();                   
    g.add((AbstractButton) bar.add(new AnalyzeButton("Analyze")));
    add(bar, BorderLayout.NORTH);

    java.util.List<MyColorRow> rows = new ArrayList<MyColorRow>();
    rows.add(new MyColorRow(10,30, null));
    rows.add(new MyColorRow(20,80, null));
    rows.add(new MyColorRow(30,70, null));
    
    tableModel = new MyColorModel(rows);       
    table.setModel(tableModel);
    
    table.setAutoCreateRowSorter(true);
    table.setShowGrid(true);
    table.getColumnModel().getColumn(0).setCellRenderer((TableCellRenderer) new MyColorRenderer());
    table.getColumnModel().getColumn(1).setCellRenderer((TableCellRenderer) new MyColorRenderer());

    add(new JScrollPane(table), BorderLayout.CENTER);
    pack();
}

//Analyze button
private class AnalyzeButton extends JButton {        
    private AnalyzeButton(String text) {
    	super(text);
        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
            	for (int i = 0; i < tableModel.getRowCount(); i++){
            		Object obj= table.getValueAt(i,0);
            		int value=(int)obj;
            		if(value==40){
            			tableModel.setColorAt(i, 0, Color.BLUE);	
            		}else if(value==50){
            			tableModel.setColorAt(i, 0, Color.RED);	
            		}else
                    tableModel.setColorAt(i, 0, null);
            	} 
            }
        });
    }
}

// The model.
private static class MyColorModel extends AbstractTableModel {
    private final java.util.List<MyColorRow> rows;

    private MyColorModel(java.util.List<MyColorRow> rows) {
        this.rows = rows;
    }
    
    @Override
	public Class<?> getColumnClass(int columnIndex) {
	    switch (columnIndex) {
	    case 0:
	    	return Integer.class;
	    case 1:
	    	return Integer.class;
	    default:    	      
	        throw new IndexOutOfBoundsException("columnIndex out of bounds");		    
	    }
	}    
    
    @Override
    public int getRowCount() {
        return rows.size();
    }

    @Override
    public int getColumnCount() {
        return 2;
    }
    
    public boolean isCellEditable(int rowIndex, int columnIndex) {  
        return true;  
    } 

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {         
    	MyColorRow cr=rows.get(rowIndex);
    	switch(columnIndex){
    	case 0:
    		return cr.getValue();
    	case 1:
    		return cr.getValue2();
    	 default: // default
 	        throw new IndexOutOfBoundsException("columnIndex out of bounds");
    	}   
    	
    }
    
    public void setValueAt(Object aValue, int rowIndex, int columnIndex){
    	MyColorRow cr=rows.get(rowIndex);
    	if(columnIndex==0)
    		cr.setValue((int)aValue);
    	else if(columnIndex==1)
    		cr.setValue2((int)aValue);
    	fireTableCellUpdated(rowIndex,columnIndex);        	
    }
    
    public Color getColorAt(int rowIndex, int columnIndex) {
    	MyColorRow cr=rows.get(rowIndex);
    	switch(columnIndex){
    	case 0:
    		return cr.getColor();
    	case 1:
    		return cr.getColor();
    	 default: // default
 	        throw new IndexOutOfBoundsException("columnIndex out of bounds");
    	}           
    }

    public void setColorAt(int rowIndex, int columnIndex, Color color) {
    	MyColorRow cr=rows.get(rowIndex);
    	if(columnIndex==0)
    		cr.setColor(color);
    	else if(columnIndex==1)
    		cr.setColor(color);       
        fireTableRowsUpdated(rowIndex, rowIndex);
    }
}

// Contains data of one row. Note that it keeps the text and the color.
private static class MyColorRow {
    int value;
    int value2;
    private Color color;
 
    private MyColorRow(int value,int value2,Color color) {
        this.value = value;
        this.value2= value2; 
        this.color = color;
    }

    public int getValue() {
        return value;
    }
    public int getValue2() {
        return value2;
    }
    public Color getColor() {
        return color;
    }
    public void setValue(int v) {
        this.value = v;
    }
    public void setValue2(int v) {
        this.value2 = v;
    }

    public void setColor(Color color) {
        this.color = color;
    }
}    

private static class MyColorRenderer extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,boolean hasFocus, int row, int column){
        MyColorModel model = (MyColorModel) table.getModel();            
        JLabel label = (JLabel) super.getTableCellRendererComponent(table, value,isSelected, hasFocus, row, column);
        Color color = model.getColorAt(row, column);  
        label.setText(value.toString());  
        label.setForeground(color != null ? color : table.getForeground());                         
        return label;
    }
}      
public void run() {
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setVisible(true);
}

} [/code]
Onde está o erro? :roll:

Seu renderer simplesmente pega a cor que está no model, sem testar valores. Se você quer o comportamento que descreveu, faça o teste no método getColorAt do TableModel.

Paul, erro claro aqui:

public void setColorAt(int rowIndex, int columnIndex, Color color) { MyColorRow cr=rows.get(rowIndex); if(columnIndex==0) cr.setColor(color); else if(columnIndex==1) cr.setColor(color); fireTableRowsUpdated(rowIndex, rowIndex); }
presumo que seja:
fireTableRowsUpdated(rowIndex, columnIndex);

Pensei que tirando do model e colocando no botão, eu teria maior flexibilidade.Tenho outras 5 classes que usam isso(pintar a célula)a diferença é a posição(algumas pegam as células da coluna 0 e 1, outras a 8 e 9…)

Corrigi!Mas recebo um Exception in thread “AWT-EventQueue-0” java.lang.IndexOutOfBoundsException: Invalid range nessa linha:

PaulH,
dá uma olhada no seu model.Tem erro claro lá.

Arrumei meu model, mas agora só pinta uma linha por vez e todas as células de uma mesma cor.O que pode estar errado?

Tô quase lá, mas preciso de um ajuda…

private class AnalyzeButton extends JButton { private AnalyzeButton(String text) { super(text); addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for (int i = 0; i < tableModel.getRowCount(); i++){ Object obj= table.getValueAt(i,0);//row,column Object obj2= table.getValueAt(i,1);//row,column int value=(int)obj; int value2=(int)obj2; if(value==40){ tableModel.setColorAt(i, 0, Color.BLUE); }else if(value==50){ tableModel.setColorAt(i, 0, Color.RED); }else if(value2==40){ tableModel.setColorAt(i, 1, Color.BLUE); }else if(value2==50){ tableModel.setColorAt(i, 1, Color.RED); }else{ tableModel.setColorAt(i, 0, null); tableModel.setColorAt(i, 1, null); } } } }); } }
Tá colorindo sempre uma única célula por linha!Se eu digitasse 40 e 50, era para colorir de azul e vermelho, mas só pega a primeira.

Você está errando na lógica dos ifs e elses. Veja:

//Se valor da coluna 0 é igual a 40, coluna 0 é azul e coluna 1 é ignorada
if(value==40){  
    tableModel.setColorAt(i, 0, Color.BLUE);

//Se valor da coluna 0 é igual a 50, coluna 0 é vermelha e coluna 1 é ignorada
}else if(value==50){  
    tableModel.setColorAt(i, 0, Color.RED);

//Se valor da coluna 0 não é igual nem a 40 nem a 50 && valor da coluna 1 é igual a 40, coluna 1 é azul e coluna 0 é ignorada
}else if(value2==40){  
    tableModel.setColorAt(i, 1, Color.BLUE);

//Se valor da coluna 0 não é igual nem a 40 nem a 50 && valor da coluna 1 é igual a 50, coluna 1 é vermelha e coluna 0 é ignorada
}else if(value2==50){  
    tableModel.setColorAt(i, 1, Color.RED);   

//Se valor da coluna 0 não é igual nem a 40 nem a 50 && valor da coluna 1  não é igual nem a 40 nem a 50, colunas 0 e 1 são ignoradas
}else{  
    tableModel.setColorAt(i, 0, null);  
    tableModel.setColorAt(i, 1, null);  
}                     

silasyudi,
como eu mudo isso?

Isoladamente, se eu tivesse apenas uma célula funcionaria OK, mas como faço para funcionar para duas(ou mais) mantendo um método só para colorir?

No botão:

public void actionPerformed(ActionEvent e) { for (int i = 0; i < tableModel.getRowCount(); i++){ Object obj= table.getValueAt(i,0);//row,column Object obj2= table.getValueAt(i,1);//row,column cellPainter(obj,obj2,tableModel,i); } } });

E o método de pintar:

public void cellPainter(Object cell1,Object cell2,TableModel tableModel,int i){ int value=(int)cell1; int value2=(int)cell2; if(value==40){ ((MyColorModel) tableModel).setColorAt(i, 0, Color.BLUE); }else if(value==50){ ((MyColorModel) tableModel).setColorAt(i, 0, Color.RED); }else if(value2==40){ ((MyColorModel) tableModel).setColorAt(i, 1, Color.BLUE); }else if(value2==50){ ((MyColorModel) tableModel).setColorAt(i, 1, Color.RED); }else{ ((MyColorModel) tableModel).setColorAt(i, 0, null); ((MyColorModel) tableModel).setColorAt(i, 1, null); } }
Como eu posso fazer algo que avalie a célula, mas sirva para várias(e poder reutilizar o código)?

Bem, como são duas colunas independentes, uma solução simples é fazer um if-else independente pra cada uma dentro do mesmo método.

[code]//Avalia só coluna 0
if(value==40){
((MyColorModel) tableModel).setColorAt(i, 0, Color.BLUE);
}else if(value==50){
((MyColorModel) tableModel).setColorAt(i, 0, Color.RED);
}else{
((MyColorModel) tableModel).setColorAt(i, 0, null);
}

//Avalia só coluna 1
if(value2==40){
((MyColorModel) tableModel).setColorAt(i, 1, Color.BLUE);
}else if(value2==50){
((MyColorModel) tableModel).setColorAt(i, 1, Color.RED);
}else
((MyColorModel) tableModel).setColorAt(i, 1, null);
} [/code]

Também pode fazer um método só pra uma coluna e chamar a função a quantidade necessária de vezes. Desta forma, caso você tenha que mudar o projeto e precisar fazer isso com mais de duas colunas é só chamar a função pra cada coluna:

[code]//Método
public void cellPainter(Object cell, TableModel tableModel, int linha, int coluna){
int value=(int)cell;
if(value==40){
((MyColorModel) tableModel).setColorAt(linha, coluna, Color.BLUE);
}else if(value==50){
((MyColorModel) tableModel).setColorAt(linha, coluna, Color.RED);
}else{
((MyColorModel) tableModel).setColorAt(linha, coluna, null);
}
}

//Chamada do método
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < tableModel.getRowCount(); i++){
for(int j = 0; j < NUMERO_DE_COLUNAS; j++){ //Caso precise de várias colunas
Object obj= table.getValueAt(i,j);//row,column
cellPainter(obj, tableModel, i, j);
}
}
});
[/code]

silasyudi,
usei o seu método e fiz assim no JButton:

public void actionPerformed(ActionEvent e) { for (int i = 0; i < tableModel.getRowCount(); i++){ for(int j=0; j<tableModel.getColumnCount();j++){ Object obj= table.getValueAt(i,j);//row,column cellPainter(obj, tableModel, i,j); } } }
Mas aparece um ClassCastException acho que devido ao getColumnCount passar por várias outras colunas de outros tipos na JTable.Por uma constante fixa não ajudaria pois só serviria para as primeiras células…como resolver isso? :?:

Se precisa somente das duas primeiras colunas, chama o método duas vezes.

[code]//Coluna == 0
Object obj= table.getValueAt(i,0);//row,column
cellPainter(obj, tableModel, i,0);

//Coluna == 1
Object obj= table.getValueAt(i,1);//row,column
cellPainter(obj, tableModel, i,1);[/code]

Paul,
já te falei: use o setColorAt.Pegue os valores(e trabalhe diretamente) do MODELO, e não da JTable.E acho mais fácil criar um método retornando a COR.

silasyudi, modifiquei a forma de colorir e funcionou!

Iron com sua ajuda e de um cara do javaranch(MArtin) consegui entender e resolver.

A solução:

private Color computeColor(Integer value) { switch (value) { case 40: return Color.BLUE; case 50: return Color.RED; default: return null; } }
E o botão:

[code]
private class AnalyzeButton extends JButton {
private AnalyzeButton(String text) {
super(text);
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < tableModel.getRowCount(); i++){
tableModel.setColorAt(i, 0, computeColor((Integer)tableModel.getValueAt(i, 0)));
tableModel.setColorAt(i, 1, computeColor((Integer)tableModel.getValueAt(i, 1)));

            } }
        });
    }
}[/code]

obrigado pela ajuda de vocês! :smiley: