JTable setValueAt StackOverflowError [英] JTable setValueAt StackOverflowError

查看:65
本文介绍了JTable setValueAt StackOverflowError的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在搜索整天,但仍然找不到问题的简单答案:当我在另一表中对其进行编辑时,如何使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侦听器,因此进入了无限循环.

有人可以解释一下我该怎么做吗?

解决方案

tableChanged在对TableModel进行更改时被调用,该更改由setValueAt方法触发并且随处可见. ..

解决方案?在TableModel ...

setValue方法内进行操作

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

例如...

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

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

}

I've been searching for whole day and I still can't find simple anwser to my problem: how can I make JTable cell update it's value when I edit it in another one?

I wanted to use somehow fireTableCellUpdated but I don't really understand how can I use it, when and on what object.

What I want is to obtain is some kind of listener that listens whether that values changes or not. In this particular scenario I have editable third column in which I store the amount and I want that listener to automatically calculate and set values in other cells in a row. I've come up with something like this:

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

But this results in StackOverflowError and I'm guessing that's because table.setValueAt fires tableChanged listener so it's going into infinite loop.

Can someone explain me how can I accomplish that?

解决方案

tableChanged is been called when a change to the TableModel is taking place, which is been trigged by the setValueAt method and around you go...

The solution? Do it inside the setValue method of the 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);
        }
    }

For example...

This is a basic example, while it only uses a single row, the idea holds true for multiple rows all the same...

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

    }

}

Updated...

It occurred to me that if the other column values are not editable, then you don't actually need to maintain their values at all, but can simply calculate them dynamically whenever getValueAt is called, for example...

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

}

这篇关于JTable setValueAt StackOverflowError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆