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);
}
}
但这会导致 WhosebugError
,我猜这是因为 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);
}
}
}
}
我已经搜索了一整天,但仍然找不到解决我的问题的简单方法:当我在另一个单元格中编辑它时,如何让 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);
}
}
但这会导致 WhosebugError
,我猜这是因为 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);
}
}
}
}