How to change the color of the lines of a JTable?

1

I need the line color to change whenever the cell value equals 0. However, all lines are being painted.

View with a simple JTable

import javax.swing.JTable;

    public class View extends javax.swing.JFrame {

        private EstoqueActionListener listener;

        public View() {
            initComponents();
            this.listener = new EstoqueActionListener(this);
        }

        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {

            jScrollPane1 = new javax.swing.JScrollPane();
            tableEstoque = new javax.swing.JTable();

            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

            tableEstoque.setModel(new javax.swing.table.DefaultTableModel(
                new Object [][] {
                    {null, null},
                    {null, null},
                    {null, null},
                    {null, null}
                },
                new String [] {
                    "PRODUTO", "STATUS"
                }
            ));
            jScrollPane1.setViewportView(tableEstoque);

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 752, Short.MAX_VALUE)
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE)
            );

            pack();
        }// </editor-fold>                        


        public static void main(String args[]) {

            try {
                for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                        javax.swing.UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(View.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (InstantiationException ex) {
                java.util.logging.Logger.getLogger(View.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                java.util.logging.Logger.getLogger(View.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                java.util.logging.Logger.getLogger(View.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }

            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new View().setVisible(true);
                }
            });
        }

        public JTable getTableEstoque() {
            return tableEstoque;
        }

        public void setTableEstoque(JTable tableEstoque) {
            this.tableEstoque = tableEstoque;
        }

        // Variables declaration - do not modify                     
        private javax.swing.JScrollPane jScrollPane1;
        private javax.swing.JTable tableEstoque;
        // End of variables declaration                   
    }

InventoryTableModel class that extends AbstractTableModel

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.table.AbstractTableModel;

public class EstoqueTableModel extends AbstractTableModel {

    List<Estoque> estoques;
    List<String> colunas = Arrays.asList("PRODUTO", "SALDO");

    public EstoqueTableModel() {
        Estoque e1 = new Estoque("ProdutoA", 10);
        Estoque e2 = new Estoque("ProdutoA", 10000);
        Estoque e3 = new Estoque("ProdutoA", 0);
        estoques = new ArrayList<>();
        estoques.add(e1);
        estoques.add(e2);
        estoques.add(e3);

    }

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

    @Override
    public int getColumnCount() {
        return colunas.size();
    }

    public int getColumnIndex(String coluna) {
        return colunas.indexOf(coluna);
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Estoque e = estoques.get(rowIndex);
        String columnName = getColumnName(columnIndex);
        switch(columnName) {
            case "PRODUTO": return e.getNomeProduto();
            case "SALDO": return e.getSaldo();
        }
        return null;
    }

    @Override
    public String getColumnName(int column) {
        return colunas.get(column);
    }



    public List<Estoque> getEstoques() {
        return estoques;
    }

    public static class Estoque {
        private String nomeProduto;
        private int saldo;

        public Estoque(String nomeProduto, int saldo) {
            this.nomeProduto = nomeProduto;
            this.saldo = saldo;
        }

        public String getNomeProduto() {
            return nomeProduto;
        }

        public void setNomeProduto(String nomeProduto) {
            this.nomeProduto = nomeProduto;
        }

        public int getSaldo() {
            return saldo;
        }

        public void setSaldo(int saldo) {
            this.saldo = saldo;
        }
    }

}

Class that manages view events and starts JTable data

import java.awt.Color;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

public class EstoqueActionListener {

    private View viewEstoque;
    private EstoqueTableModel tableEstoque;

    public EstoqueActionListener(View viewEstoque) {
        this.viewEstoque = viewEstoque;
        startTable();
    }

    public void startTable() {
        tableEstoque = new EstoqueTableModel();
        if (tableEstoque.getEstoques() != null) {
            viewEstoque.getTableEstoque().setModel(tableEstoque);

            for (int i = 0; i < tableEstoque.getEstoques().size(); i++) {
                int cellValue = (int) tableEstoque.getValueAt(i, tableEstoque.getColumnIndex("SALDO"));
                if (cellValue == 0) { //SE 0, MUDE A COR
                    viewEstoque.getTableEstoque().setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
                        @Override
                        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                            final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                            c.setBackground(Color.red);
                            c.setForeground(Color.white);
                            return c;
                        }
                    });
                }
            }
        }
    }
    
asked by anonymous 05.05.2017 / 22:43

1 answer

1

First you need to understand what the below call means:

viewEstoque.getTableEstoque().setDefaultRenderer(Object.class, new 
                                    DefaultTableCellRenderer(){..});

This method expects the type of table cells and a renderer that will be applied to cells with that type reported. Although Object is the parent class of all classes in java, there is a peculiarity in the operation of swing renderers, where it applies certain rendering according to the types below:

  
  • Boolean - renders with a checkbox.
  •   
  • Number - renders a right-aligned Label.
  •   
  • Double , Float - same as Number, but the object-to-text conversion is performed by an instance of NumberFormat (using the default number format for the current location).
  •   
  • Date - renders a Label, with the conversion of "object-to-text" performed by an instance of DateFormat (using a short style for date and time).
  •   
  • ImageIcon , Icon - rendered with a centralized Label.
  •   
  • Object - rendered by a Label that displays string value of the object.
  •   

As the value of your saldo column is of numeric type, when rendering, java will take the type into account and apply the second option in the list above, completely ignoring what you defined in the renderer.

To solve this, the simplest way is to define a renderer directly to the numeric column, instead of applying to the whole table:

// no lugar do "1" é preciso informar o indice da coluna desejada
table.getColumnModel().getColumn(1).setCellRenderer(new SaldoTableRenderer());

In this case, I chose to create the class SaldoTableRenderer , and we need to save the default background and foreground values, because when the value is not zero, you can restore these values in the following cell:

public class SaldoTableRenderer extends DefaultTableCellRenderer {

    Color defaultBackground, defaultForeground;

    public SaldoTableRenderer() {
        this.defaultBackground = getBackground();
        this.defaultForeground = getForeground();
        setHorizontalAlignment(javax.swing.JLabel.RIGHT);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            Integer saldo = (int) value;

            if (saldo == 0) {
                c.setBackground(Color.red);
                c.setForeground(Color.white);
            }else{
                c.setBackground(defaultBackground);
                c.setForeground(defaultForeground);             
            }
            setText(saldo.toString());

        return c;
    }

}

And the result:

Ifyou'dliketoseeitinpracticebeforeapplyingtoyourcode,I'vedonea testable example , just compile.

To color the entire line, you must override the prepareRenderer method of the table, using the same logic as the previous renderer of the response:

    table = new JTable(new EstoqueTableModel()) {
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {

            final Component c = super.prepareRenderer(renderer, row, column);

            Integer saldo = (int) table.getValueAt(row, 1);

            if (saldo <= 0) {
                c.setBackground(Color.red);
                c.setForeground(Color.white);
            } else {
                c.setBackground(table.getBackground());
                c.setForeground(table.getForeground());
            }
            return c;
        }
    };

References:

Concepts: Editors and Renderers

    
06.05.2017 / 21:37