1

I'm populating a Jtable with yahoo quotes whose download is scheduled every 60 seconds subclassing Timertask. I would change background of single cells depending on new values (e.g. if new value is less than old value background color becomes green).

I've tried (with no result) to implement a TableCellRenderer adding a propertychangelistener as follows:

    private static class TextCellRenderer implements TableCellRenderer {

        @Override public Component getTableCellRendererComponent(final JTable table, Object value, boolean isSelected, boolean hasFocus, final int r, final int c) {
            table.setRowHeight(10);
            final JTextField text = new JTextField();
            text.setOpaque(true);//disable transparence
            text.setText(value.toString());
            text.addPropertyChangeListener(new PropertyChangeListener(){

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                   JTextField l=(JTextField)evt.getSource();
                   l.setOpaque(true);
                   String property=evt.getPropertyName();
                   double d1=(Double)evt.getOldValue();//old value
                   if(property=="text"){
                       double d2=(Double)evt.getNewValue();//new value
                       //nothing happens - mantain old backround
                       if (d2==d1){
                           l.setBackground(l.getBackground());
                       }
                       //new value>old value
                       else if (d2>d1){
                           l.setBackground(Color.green);
                       }
                       //new value<old value
                       else if (d2<d1){
                           l.setBackground(Color.red);
                       } 
                       d1=d2;
                       }

                }

            });
            return text;  
        }       
}

I need to understand what i'm doing wrong. Thanks

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
ttrash
  • 60
  • 8
  • the line "d1=d2" at the end is obviously wrong, it was written in previous code, i forgot to delete it. – ttrash Sep 02 '14 at 13:05
  • 1
    See this [`if(property=="text")`](http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – Paul Samsotha Sep 02 '14 at 13:30
  • 1
    Also not a good idea to 1. Mess with the table (table.setRowHeight) from the renderer. Table passed to the method should generally be used for read-only. 2. Creating the renderer component inside the get renderer method. – Paul Samsotha Sep 02 '14 at 13:32
  • silly me... it was .equals("text"). I forgot the constructor,made it and put inside the textfield instance. I tried to print property and newValue but it returns nothing. The problem is how handle the event.. – ttrash Sep 02 '14 at 14:37

1 Answers1

1

My suggestion is to first make use of some good ol oop design. Create a simple bean like CurrentPrevious where you can hold the value of the current and previous values. Add those instances of CurrentPrevious to the table model.

class CurrentPrevious {
    Double current;
    Double previous;
    // getters an setters

    @Override
    public String toString() {
        return current.toString();
    }
}

You only want the current value to be viewed in the table, so you can simply override the toString of the class and return the string value of current.

As for the rendering, you don't need a custom render. I don't see you doing anything special with it. Creating a text field as the renderer seems pointless. Any desired editing functionality should be done with the editor component. So instead of the custom renderer, just override prepareRenderer of the table. Something like below:

@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
    Component c = super.prepareRenderer(renderer, row, col);
    Object value = getValueAt(row, col);
    if (value instanceof CurrentPrevious) {
        CurrentPrevious curPrev = (CurrentPrevious) value;
        Double current = curPrev.getCurrent();
        Double previous = curPrev.getPrevious();
        Color color = getColor(current, previous);
        c.setBackground(color);
    }
    return c;
}
...
private Color getColor(Double current, Double previous) {
    Color color;
    if (current.equals(previous) || current > previous) {
        color = Color.GREEN;
    } else {
        color = Color.RED;
    }
    return color;
}

When the value needs to be updated, just change the value of the previous to the current, and set the current to the new value.

DefaultTableModel model = (DefaultTableModel) table.getModel();
for (int i = 0; i < model.getRowCount(); i++) {
    CurrentPrevious curPrev = (CurrentPrevious) model.getValueAt(i, 0);
    curPrev.setPrevious(curPrev.getCurrent());
    Double newCurrent = getRandomDouble();
    curPrev.setCurrent(newCurrent);
    model.setValueAt(curPrev, i, 0);
}

The result would look something like:

enter image description here

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

public class ChangeCellColorDemo {

    private Random random = new Random();
    private JTable table = getTable();

    public ChangeCellColorDemo() {
        Timer timer = new Timer(2000, new TimerListener());
        JFrame frame = new JFrame();
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        timer.start();
    }

    private class TimerListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            DefaultTableModel model = (DefaultTableModel) table.getModel();
            for (int i = 0; i < model.getRowCount(); i++) {
                CurrentPrevious curPrev = (CurrentPrevious) model.getValueAt(i, 0);
                curPrev.setPrevious(curPrev.getCurrent());
                Double newCurrent = getRandomDouble();
                curPrev.setCurrent(newCurrent);
                model.setValueAt(curPrev, i, 0);
            }
        }
    }

    private JTable getTable() {
        JTable table = new JTable(getTableModel()) {
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
                Component c = super.prepareRenderer(renderer, row, col);
                Object value = getValueAt(row, col);
                if (value instanceof CurrentPrevious) {
                    CurrentPrevious curPrev = (CurrentPrevious) value;
                    Double current = curPrev.getCurrent();
                    Double previous = curPrev.getPrevious();
                    Color color = getColor(current, previous);
                    c.setBackground(color);
                }
                return c;
            }

            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return getPreferredSize();
            }
        };
        return table;
    }

    private TableModel getTableModel() {
        String[] cols = {"Value"};
        DefaultTableModel model = new DefaultTableModel(cols, 0);
        for (int i = 0; i < 10; i++) {
            Object[] row = new Object[1];
            Double current = getRandomDouble();
            Double previous = getRandomDouble();
            row[0] = new CurrentPrevious(current, previous);
            model.addRow(row);
        }
        System.out.println(model.getRowCount());
        return model;
    }

    private Color getColor(Double current, Double previous) {
        Color color;
        if (current.equals(previous) || current > previous) {
            color = Color.GREEN;
        } else {
            color = Color.RED;
        }
        return color;
    }

    private Double getRandomDouble() {
        BigDecimal bd = new BigDecimal(random.nextDouble());
        bd = bd.setScale(4, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }

    class CurrentPrevious {

        Double current;
        Double previous;

        public CurrentPrevious() {}

        public CurrentPrevious(Double current, Double previous) {
            this.current = current;
            this.previous = previous;
        }

        public Double getCurrent() {
            return current;
        }

        public Double getPrevious() {
            return previous;
        }

        public void setCurrent(Double current) {
            this.current = current;
        }

        public void setPrevious(Double previous) {
            this.previous = previous;
        }

        @Override
        public String toString() {
            return current.toString();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ChangeCellColorDemo();
            }
        });
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 1
    thanks a lot, it works fine. I didn' t know about beans. Thanks. – ttrash Sep 02 '14 at 20:44
  • Cool glad to help. "bean" is actually short for JavaBean. It's just a [plain old java object (POJO)](http://en.wikipedia.org/wiki/Plain_Old_Java_Object) that follows [JavaBean conventions](http://en.wikipedia.org/wiki/JavaBeans) – Paul Samsotha Sep 03 '14 at 02:06