So this is very basic proof of concept.
What this does is, it maintains an internal "proxy"/"cache" of the values which are added/removed/updated. In this way, it's capable of using the event information to simply update the tally without having to re-iterate the model, which can be time consuming.
When you create a new model, you should attach the the listener to it, BEFORE populating it, this will give the listener the opportunity to be notified of new rows and update its internal state.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultTableModel model;
private JTextField tallyField;
private double tally;
public TestPane() {
setLayout(new BorderLayout());
tallyField = new JTextField(10);
// When loading data, update this value to represent the total
// of the data that was loaded
tally = 0;
model = new DefaultTableModel(new Object[]{"Quanity", "Amount"}, 0);
model.addTableModelListener(new TableModelListener() {
private Map<Integer, Double> proxy = new HashMap<Integer, Double>();
private NumberFormat nf = NumberFormat.getCurrencyInstance();
@Override
public void tableChanged(TableModelEvent e) {
int firstRow = e.getFirstRow();
int lastRow = e.getLastRow();
double sum = 0;
for (int row = Math.min(firstRow, lastRow); row <= Math.max(firstRow, lastRow); row++) {
switch (e.getType()) {
case TableModelEvent.DELETE:
sum -= delete(row);
break;
case TableModelEvent.INSERT:
sum += add(row);
break;
case TableModelEvent.UPDATE:
sum -= delete(row);
sum += add(row);
break;
default:
break;
}
}
tally += sum;
tallyField.setText(nf.format(tally));
}
protected double delete(int row) {
double sum = 0;
if (proxy.containsKey(row)) {
sum = proxy.get(row);
}
proxy.remove(row);
return sum;
}
protected double add(int row) {
int qty = 0;
// I hope your model is better setup then mine
if (model.getValueAt(row, 0) instanceof String) {
qty = Integer.parseInt((String) model.getValueAt(row, 0));
} else if (model.getValueAt(row, 0) instanceof Integer) {
qty = (Integer) model.getValueAt(row, 0);
}
double amount = (Double) model.getValueAt(row, 1);
double rowTotal = qty * amount;
proxy.put(row, rowTotal);
return rowTotal;
}
});
JTable table = new JTable(model);
add(new JScrollPane(table));
JButton add = new JButton("Add");
JButton delete = new JButton("Delete");
add.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
double amount = 1.0; //Math.random() * 1000.0;
model.addRow(new Object[]{1, amount});
}
});
delete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] rows = table.getSelectedRows();
List<Integer> selection = new ArrayList<>(rows.length);
for (int row : rows) {
selection.add(row);
}
Collections.sort(selection);
Collections.reverse(selection);
for (int row : selection) {
model.removeRow(row);
}
}
});
JPanel footer = new JPanel(new BorderLayout());
JPanel buttons = new JPanel();
buttons.add(add);
buttons.add(delete);
footer.add(tallyField);
footer.add(buttons, BorderLayout.SOUTH);
add(footer, BorderLayout.SOUTH);
}
}
}
Why do we need the cache? Because when a row is deleted, you can no longer determine it's value.
If I was doing this in a real application, I would make a simple class that implements TableModelListener
but which provided a getter to retrieve the tally
(so the tally
becomes encapsulated to the class) and use an observer pattern to generate notifications when the tally
is changed.
This means that when you load the data, you can update the model and "tally monitor" independently. And when you need to, update the required fields on the screen.
This can become very complicated, very quickly. I did something simular to produce a automatic tallying system for tallying rows and columns independently of the model, so the system wasn't coupled to the model structure apart from known which row/column it was responsible for tallying