JTable setValueAt StackOverflowError

时间:2016-02-20 00:01:26

标签: java swing jtable stack-overflow

我一直在寻找一整天,我仍然找不到简单的问题:当我在另一个编辑时,如何让JTable单元更新它的值?

我想以某种方式使用fireTableCellUpdated,但我真的不明白如何使用它,何时以及在什么对象上使用它。

我想要的是获得某种倾听者是否会听取这些值的变化。在这个特殊情况下,我有可编辑的第三列,其中我存储了金额,我希望该监听器自动计算并设置一行中其他单元格中的值。我想出了类似的东西:

@Override
public void tableChanged(TableModelEvent e)
{
    BigDecimal withoutTax, tax, withTax;

    for(int i = 0; i < table.getRowCount(); i++)
    {
            BigDecimal amount = new BigDecimal(String.valueOf(table.getValueAt(i, 3)).replace(",", "."));
            BigDecimal price = new BigDecimal(String.valueOf(table.getValueAt(i, 4)).replace(",", "."));
            withoutTax = amount.multiply(price, new MathContext(2));
            table.setValueAt(withoutTax, i, 5);
            tax = withoutTax.multiply(new BigDecimal(0.23), new MathContext(2));
            table.setValueAt(tax, i, 7);
            withTax = withoutTax.add(tax, new MathContext(2));
            table.setValueAt(withTax, i, 8);
    }
}

但是这会产生StackOverflowError,我猜这是因为table.setValueAt会触发tableChanged侦听器,所以它会进入无限循环。

有人可以解释我如何实现这一目标?

1 个答案:

答案 0 :(得分:3)

tableChanged发生变化时,{p> TableModel被调用,该变更由setValueAt方法触发,并且在您周围...

解决方案?在setValue ...

TableModel方法中进行
public class TestModel extends ... { // Some TableModel

    //...

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == 3) {
            // Set the value been passed to in (probably from the editor)...            
            fireTableCellUpdated(rowIndex, columnIndex);
            BigDecimal amount = new BigDecimal(String.valueOf(getValueAt(rowIndex, 3)).replace(",", "."));
            BigDecimal price = new BigDecimal(String.valueOf(getValueAt(rowIndex, 4)).replace(",", "."));
            BigDecimal withoutTax = amount.multiply(price, new MathContext(2));
            // Set the value for row x 5 directly within the backing store of the model...
            //table.setValueAt(withoutTax, i, 5);
            BigDecimal tax = withoutTax.multiply(new BigDecimal(0.23), new MathContext(2));
            // Set the value for row x 7 directly within the backing store of the model...
            //table.setValueAt(tax, i, 7);
            BigDecimal withTax = withoutTax.add(tax, new MathContext(2));
            // Set the value for row x 8 directly within the backing store of the model...
            //table.setValueAt(withTax, i, 8);

            fireTableCellUpdated(rowIndex, 5);
            fireTableCellUpdated(rowIndex, 7);
            fireTableCellUpdated(rowIndex, 8);

            // It might actually be easier to use...
            //fireTableRowsUpdated(rowIndex, rowIndex);
        }
    }

例如......

这是一个基本的例子,虽然它只使用一行,但这个想法适用于所有相同的多行......

Table

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JTable table = new JTable(new MultiplicationTableMode());
            add(new JScrollPane(table));
        }

    }

    public class MultiplicationTableMode extends AbstractTableModel {

        private List<List<Integer>> values;

        public MultiplicationTableMode() {
            values = new ArrayList<>(1);
            List<Integer> cols = new ArrayList<>(11);
            for (int index = 0; index < 11; index++) {
                cols.add(0);
            }
            values.add(cols);
        }

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

        @Override
        public int getColumnCount() {
            return 10;
        }

        @Override
        public String getColumnName(int column) {
            return column == 0 ? "?" : "x" + Integer.toString(column);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == 0;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            List<Integer> columns = values.get(rowIndex);
            return columns.get(columnIndex);
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return Integer.class;
        }


        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            if (columnIndex == 0) {
                if (aValue instanceof Integer) {
                    List<Integer> columns = values.get(rowIndex);
                    int intValue = (int) aValue;
                    columns.set(0, intValue);
                    for (int index = 1; index < columns.size(); index++) {
                        columns.set(index, intValue * index);
                    }
                    fireTableRowsUpdated(rowIndex, rowIndex);
                }
            }
        }

    }

}

更新...

我想到,如果其他列值不可编辑,那么您实际上根本不需要维护它们的值,但只要在调用getValueAt时就可以动态地计算它们,例如..

public class MultiplicationTableMode extends AbstractTableModel {

    private List<Integer> values;

    public MultiplicationTableMode() {
        values = new ArrayList<>(1);
        values.add(0);
    }

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

    @Override
    public int getColumnCount() {
        return 10;
    }

    @Override
    public String getColumnName(int column) {
        return column == 0 ? "?" : "x" + Integer.toString(column);
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == 0;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        int value = values.get(rowIndex);
        if (columnIndex > 0) {
            value *= columnIndex;
        }
        return value;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return Integer.class;
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == 0) {
            if (aValue instanceof Integer) {
                values.set(rowIndex, (int)aValue);
                fireTableRowsUpdated(rowIndex, rowIndex);
            }
        }
    }

}