Dan
Dan

Reputation: 7734

How to fire `fireTableDataChanged` properly

I have a reasonable simple JTable that I want to update every second.

To do this, I found that you are supposed to use fireTableDataChanged(), however, after several attempts I still can't get it to work.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;

@SuppressWarnings("serial")
public class Changer extends JFrame {
    int phils = 1;
    int daves = 2;
    int bobs = 3;
    String[] cols = {"Name", "Num"};
    Object[][] data = {{"Phil", phils},
                       {"Dave", daves},
                       {"Bob", bobs}};
    DefaultTableModel dm = new DefaultTableModel(data, cols);
    JTable table = new JTable(dm);

    public Changer() {
        super("Changer");

        JPanel p = new JPanel();
        p.add(new JScrollPane(table));
        add(p);
        pack();
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        Timer t = new Timer(1000, new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                phils++;
                daves++;
                bobs++;
                data = new Object[][] {{"Phil", phils},
                           {"Dave", daves},
                           {"Bob", bobs}};
                dm.fireTableDataChanged();
                repaint();
                revalidate();
                table.repaint();
                table.revalidate();

                System.out.println(phils + ", " + daves + ", " + bobs);
            }
        });

        t.start();
    }

    public static void main(String[] args) {
        new Changer();
    }
}

To begin with I just had

Timer t = new Timer(1000, new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e) {
        phils++;
        daves++;
        bobs++;

        dm.fireTableDataChanged();
    }
});

As the docs seem to suggest it would work if the values are updated.

Then I tried

Timer t = new Timer(1000, new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e) {
        phils++;
        daves++;
        bobs++;

        data = new Object[][] {{"Phil", phils},
               {"Dave", daves},
               {"Bob", bobs}};
        dm.fireTableDataChanged();
    }
});

To see if I had to physically update the variable data.

Finally I added all the validates and repaints to see if that would make a difference, and a System.out.println just incase I had made a mistake with the timer somehow. What am I doing wrong?

Upvotes: 1

Views: 1451

Answers (1)

Arnaud
Arnaud

Reputation: 17534

The DefaultTableModel you are using creates a Vector from your Object array and uses that as its data :

protected static Vector convertToVector(Object[][] anArray) {
        if (anArray == null) {
            return null;
        }
        Vector<Vector> v = new Vector<Vector>(anArray.length);
        for (Object[] o : anArray) {
            v.addElement(convertToVector(o));
        }
        return v;
    }

This means that replacing the array (or even modifying its content) won't magically reflect any change to the model.

What you can do before calling dm.fireTableDataChanged() is either

Replace the data of the model

dm.setDataVector(data, cols);

Or delete all rows and insert the new ones

while(dm.getRowCount()>0){
       dm.removeRow(0);
}
dm.addRow(new Object[] {"Phil", phils });
dm.addRow(new Object[] {"Dave", daves  });
dm.addRow(new Object[] {"Bob", bobs });

Or search for the row matching the name, then update the row :

updateRow(new Object[] {"Phil", phils });
updateRow(new Object[] {"Dave", daves  });
updateRow(new Object[] {"Bob", bobs });

with this updateRow method largely inspired from Java JTable update row :

private void updateRow( Object[] data) {

        String userName = data[0].toString();
        for (int i = 0; i < dm.getRowCount(); i++)
            if (dm.getValueAt(i, 0).equals(userName))
                for (int j = 1; j < data.length; j++)
                    dm.setValueAt(data[j], i, j);
    }

Upvotes: 2

Related Questions