JTable setValueAt StackOverflowError [英] 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屋!