java - JTable setValueAt StackOverflowError

标签 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 监听器,因此它进入了无限循环。

有人可以解释一下我怎样才能做到这一点吗?

最佳答案

tableChangedTableModel 发生更改时被调用,这是由 setValueAt 方法触发的,并且在您周围...

解决方案?在 TableModelsetValue 方法中执行此操作...

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);
            }
        }
    }

}

关于java - JTable setValueAt StackOverflowError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35517299/

相关文章:

java - 如何让我的程序(彩票游戏)再次检查提示?

java - 最简单的云存储服务API

java - 数据未通过 jtable 保存在数据库中(已编辑)

Java/Swing : Obtain Window/JFrame from inside a JPanel

java - 如何使用 AbstractTableModel 在 JTable 上添加行?

java - PowerMockRunner 在运行简单测试时导致 ExceptionInInitializerError

java - 如何在 JTable 中用不同颜色绘制特定列?

java - 如何从jTable中删除数据库中的数据?

java - 如何更改 JTable 的标题背景颜色?

java - 如何增加 ODBC 中的字符限制