javaswingjtabletablecellrenderer

Old value in JTable within DefualtTableCellRenderer


I've been trying for weeks to get a Cell in a JTable to flash by changing the background color with RGBtoHSV, I am able to flash a single row using a method I found searching online but when it comes to multiple rows it does not seem to work.

I've tried creating a custom renderer class but still unable to find a way to have the old value with the new value to flash the cell when oldvalue < newvalue.

All my attempts on getting the oldValue for the cell has failed so its not in there. I have tried TableCellListener, a class I found using my googlefu but I had no idea how to implement this into a renderer, although implementing it to a table was successful.

DefaultTableCellRenderer cTaxas = new DefaultTableCellRenderer() {

        Color gray = new Color(212, 212, 212);

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

            if (table.getValueAt(row, column) != null && !isSelected && (row % 2) == 0) {
                cellComponent.setBackground(gray);
                cellComponent.setForeground(Color.BLACK);
            } else if (table.getValueAt(row, column) != null && !isSelected) {
                cellComponent.setBackground(Color.WHITE);
                cellComponent.setForeground(Color.BLACK);
            }

            return cellComponent;
        }

    };
    cTaxas.setHorizontalAlignment(SwingConstants.CENTER);

EDIT: I'm trying to achieve something like the TableCellListener.class I have found while searching, but within the DefaultCellRenderer.

public class TableCellListener implements PropertyChangeListener, Runnable {
private JTable table;
private Action action;

private int row;
private int column;
private Object oldValue;
private Object newValue;

/**
 * Create a TableCellListener.
 *
 * @param table
 *            the table to be monitored for data changes
 * @param action
 *            the Action to invoke when cell data is changed
 */
public TableCellListener(JTable table, Action action) {
    this.table = table;
    this.action = action;
    this.table.addPropertyChangeListener(this);
}

/**
 * Create a TableCellListener with a copy of all the data relevant to the
 * change of data for a given cell.
 *
 * @param row
 *            the row of the changed cell
 * @param column
 *            the column of the changed cell
 * @param oldValue
 *            the old data of the changed cell
 * @param newValue
 *            the new data of the changed cell
 */
private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) {
    this.table = table;
    this.row = row;
    this.column = column;
    this.oldValue = oldValue;
    this.newValue = newValue;
}

/**
 * Get the column that was last edited
 *
 * @return the column that was edited
 */
public int getColumn() {
    return column;
}

/**
 * Get the new value in the cell
 *
 * @return the new value in the cell
 */
public Object getNewValue() {
    return newValue;
}

/**
 * Get the old value of the cell
 *
 * @return the old value of the cell
 */
public Object getOldValue() {
    return oldValue;
}

/**
 * Get the row that was last edited
 *
 * @return the row that was edited
 */
public int getRow() {
    return row;
}

/**
 * Get the table of the cell that was changed
 *
 * @return the table of the cell that was changed
 */
public JTable getTable() {
    return table;
}

//
// Implement the PropertyChangeListener interface
//
@Override
public void propertyChange(PropertyChangeEvent e) {
    // A cell has started/stopped editing

    if ("tableCellEditor".equals(e.getPropertyName())) {
        if (table.isEditing())
            processEditingStarted();
        else
            processEditingStopped();
    }
}

/*
 * Save information of the cell about to be edited
 */
private void processEditingStarted() {
    // The invokeLater is necessary because the editing row and editing
    // column of the table have not been set when the "tableCellEditor"
    // PropertyChangeEvent is fired.
    // This results in the "run" method being invoked

    SwingUtilities.invokeLater(this);
}

/*
 * See above.
 */
@Override
public void run() {
    row = table.convertRowIndexToModel(table.getEditingRow());
    column = table.convertColumnIndexToModel(table.getEditingColumn());
    oldValue = table.getModel().getValueAt(row, column);
    newValue = null;
}

/*
 * Update the Cell history when necessary
 */
private void processEditingStopped() {
    newValue = table.getModel().getValueAt(row, column);

    // The data has changed, invoke the supplied Action

    if (!newValue.equals(oldValue)) {
        // Make a copy of the data in case another cell starts editing
        // while processing this change

        TableCellListener tcl = new TableCellListener(getTable(), getRow(), getColumn(), getOldValue(),
                getNewValue());

        ActionEvent event = new ActionEvent(tcl, ActionEvent.ACTION_PERFORMED, "");
        action.actionPerformed(event);
    }
}

}

I used the celllistener in my JTable like this:

Action action = new AbstractAction() {
public void actionPerformed(ActionEvent e)
{
    TableCellListener tcl = (TableCellListener)e.getSource();
    System.out.println("Row   : " + tcl.getRow());
    System.out.println("Column: " + tcl.getColumn());
    System.out.println("Old   : " + tcl.getOldValue());
    System.out.println("New   : " + tcl.getNewValue());
}
};
TableCellListener tcl = new TableCellListener(table, action);

I'm trying to get this class working within a JTables Renderer so I can flash the Cell when the old value is less than the new value that has been pulled from a database.

EDIT2: I'm hoping someone can shed some light as to how I can get the old value within the renderer or a different method of flashing cell tables.


Solution

  • You seem to have two requirements

    1. how I can get the old value
    2. but when it comes to multiple rows it does not seem to work.

    Not really sure how they are related. You can only change a single value in the editor at one time, so I would think you only ever need to blink the row color for one row at a time.

    Anyway here is some (decade) old code that allows you to specify multiple cells, rows or columns to blink with different colors all at the same time. The blinking continues until you stop it yourself.

    So I guess you could use the TableCellListener to add a new cell/row to the blinding renderer.

    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.Timer;
    import javax.swing.*;
    import javax.swing.table.*;
    
    public class TableColor extends JFrame
    {
        ColorRenderer colorRenderer;
    
        public TableColor()
        {
            String[] columnNames = {"Date", "String", "Integer", "Boolean"};
            Object[][] data =
            {
                {new Date(), "A", new Integer(1), new Boolean(true)},
                {new Date(), "B", new Integer(2), new Boolean(false)},
                {new Date(), "C", new Integer(3), new Boolean(true)},
                {new Date(), "D", new Integer(4), new Boolean(false)}
            };
    
            final DefaultTableModel model = new DefaultTableModel(data, columnNames);
            final JTable table = new JTable( model )
            {
                public Class getColumnClass(int column)
                {
                    return getValueAt(0, column).getClass();
                }
    
                //  Apply background to existing renderer
    
                public Component prepareRenderer(
                    TableCellRenderer renderer, int row, int column)
                {
                    Component c = super.prepareRenderer(renderer, row, column);
                    colorRenderer.setBackground(c, row, column);
                    return c;
                }
            };
            table.setPreferredScrollableViewportSize(table.getPreferredSize());
            JScrollPane scrollPane = new JScrollPane( table );
            getContentPane().add( scrollPane );
    
            JButton button = new JButton( "Add Row" );
            button.addActionListener( new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
    //              model.addRow( createRow() );
                    model.setRowCount( model.getRowCount() + 1 );
                    int row = table.getRowCount() - 1;
    //              table.changeSelection(row, 0, false, false);
                    table.requestFocusInWindow();
                    colorRenderer.setRowColor(row, Color.YELLOW);
                }
            });
            getContentPane().add(button, BorderLayout.SOUTH);
    
            //  Create blinking color renderer
    
            colorRenderer = new ColorRenderer( table );
            colorRenderer.setCellColor(1, 1, Color.RED);
            colorRenderer.setRowColor(1, Color.GREEN);
            colorRenderer.setRowColor(3, Color.GREEN);
            colorRenderer.setColumnColor(1, Color.BLUE);
            colorRenderer.startBlinking(1000);
        }
    
        public static void main(String[] args)
        {
            TableColor frame = new TableColor();
            frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
            frame.pack();
            frame.setVisible(true);
        }
    
        /*
        **  Color cell background
        */
        class ColorRenderer implements ActionListener
        {
            private JTable table;
            private AbstractTableModel model;
            private Map colors;
            private boolean isBlinking = true;
            private Timer timer;
            private Point location;
    
            public ColorRenderer(JTable table)
            {
                this.table = table;
                model = (AbstractTableModel)table.getModel();
                colors = new HashMap();
                location = new Point();
            }
    
            public void setBackground(Component c, int row, int column)
            {
                //  Don't override the background color of a selected cell
    
                if ( table.isCellSelected(row, column) ) return;
    
                //  The default render does not reset the background color
                //  that was set for the previous cell, so reset it here
    
                if (c instanceof DefaultTableCellRenderer)
                {
                    c.setBackground( table.getBackground() );
                }
    
                //  Don't highlight this time
    
                if ( !isBlinking ) return;
    
                //  In case columns have been reordered, convert the column number
    
                column = table.convertColumnIndexToModel(column);
    
                //  Get cell color
    
                Object key = getKey(row, column);
                Object o = colors.get( key );
    
                if (o != null)
                {
                    c.setBackground( (Color)o );
                    return;
                }
    
                //  Get row color
    
                key = getKey(row, -1);
                o = colors.get( key );
    
                if (o != null)
                {
                    c.setBackground( (Color)o );
                    return;
                }
    
                //  Get column color
    
                key = getKey(-1, column);
                o = colors.get( key );
    
                if (o != null)
                {
                    c.setBackground( (Color)o );
                    return;
                }
    
            }
    
            public void setCellColor(int row, int column, Color color)
            {
                Point key = new Point(row, column);
                colors.put(key, color);
            }
    
            public void setColumnColor(int column, Color color)
            {
                setCellColor(-1, column, color);
            }
    
            public void setRowColor(int row, Color color)
            {
                setCellColor(row, -1, color);
            }
    
            private Object getKey(int row, int column)
            {
                location.x = row;
                location.y = column;
                return location;
            }
    
            public void startBlinking(int interval)
            {
                timer = new Timer(interval, this);
                timer.start();
            }
    
            public void stopBlinking()
            {
                timer.stop();
            }
    
            public void actionPerformed(ActionEvent e)
            {
                isBlinking = !isBlinking;
    
                Iterator it = colors.keySet().iterator();
    
                while ( it.hasNext() )
                {
                    Point key = (Point)it.next();
                    int row = key.x;
                    int column = key.y;
    
                    if (column == -1)
                    {
                        model.fireTableRowsUpdated(row, row);
                    }
                    else if (row == -1)
                    {
                        int rows = table.getRowCount();
    
                        for (int i = 0; i < rows; i++)
                        {
                            model.fireTableCellUpdated(i, column);
                        }
                    }
                    else
                    {
                        model.fireTableCellUpdated(row, column);
                    }
                }
            }
        }
    }