Пользовательские ячейки в JTable, возвращающие неправильные значения (и не отображающиеся правильно)
Я пишу собственную систему POS и позволяю пользователям изменять количество прямо на столе, вместо того, чтобы создавать диалоговое окно. Вот первая строка таблицы с фиктивными данными в ней. (Я еще не связал его с БД.)
Price и Amt имеют собственные средства визуализации ячеек (не то, что кто-либо может сказать, потому что они не рендерится правильно) для отображения формата денег. РЕДАКТИРОВАТЬ: я отменил пользовательские редакторы (не рендеры, спасибо), и проблема ниже все еще сохраняется
План состоял в том, чтобы, когда пользователь вводил количество в поле qty, он обновлял ячейку amt (и промежуточную сумму в верхней части экрана). Для этого я создал верификатор, который проверял бы, что данные были целыми числами, и прикрепил его к пользовательскому редактору ячеек, который просто добавляет верификатор в JTextField.
Мой верификатор, который вложен в сам экран, поэтому у него был доступ к полям экрана и самой таблице. Я хотел поместить это в модель стола, но обнаружил, что не могу сказать, какую ячейку выбрал пользователь. (Verify() работает, поэтому я опускаю это)
class QtyVerifier extends InputVerifier
{
private int qty = 0;
private BigDecimal pricePerUnit;
private BigDecimal prevamt;
@Override
public boolean shouldYieldFocus(JComponent input)
{
//reset quantity
this.qty = 0;
//verify the results
boolean ok2go = this.verify(input);
if (ok2go)
{
//grab all the current values
this.qty = new Integer(
(String)salesOrderFormTable.getValueAt(rowselected, colselected));
this.pricePerUnit = new BigDecimal(
(String)salesOrderFormTable.getValueAt(rowselected, colselected));
this.prevamt = new BigDecimal(
(String)salesOrderFormTable.getValueAt(rowselected, colselected));
//remove previous amount from the total
addLineCostToRunningTotal(this.prevamt.negate());
//update sales order total
addLineCostToRunningTotal(amt);
//update line total cell
salesOrderFormTable.setValueAt(actualTotal, rowselected, 6);
salesOrderFormTable.validate();
}
else { ... }
return ok2go;
}
....
};
Вот где это становится действительно странным. Я говорю количеству, что количество все еще 1. Это вытягивает все надлежащие данные из ячеек.
Selected: 0,1
Quantity in cell 0,1 is 1
Line price is 1.0
Previous amt is 1.0
New amt is 1.0
Ладно, хорошо. Поэтому я иду, чтобы изменить значение на 5 и нажимаю Enter.Он изменяет значение на 25 (решил эту проблему), а также извлекает неверные данные из ячеек.
Quantity in cell 0,1 is 5 //this is correct
Line price is 5.0 //this is incorrect. It should be 1.
Previous amt is 5.0 //also incorrect. This also should be 1.
New amt is 25.0 //this WOULD be correct if the previous data was.
Что происходит с моим столом?! Почему это дает мне совершенно неверную информацию в двух ячейках и заменяет кол-во на то, что я не набрал? Есть ли более простой способ сделать это (с или без верификатора)? У меня не может быть неправильной стоимости, подходящей для формы заказа. Я решил изменить все мои значения с двойных на большие десятичные, чтобы избежать ошибок округления, но это даже не ошибка округления. Это просто неправильно. Я полностью потерян сейчас.
1 ответ
Учитывая, что ваш код сильно настроен и его трудно отлаживать без объединения всех частей, я бы начал с нуля, обращаясь к базовым концепциям и говоря, что эти требования могут быть удовлетворены при работе с TableModel:
- Проверьте ввод числовых столбцов: это можно делегировать нужному редактору, если getColumnClass (...) в нашей табличной модели возвращает соответствующий класс. Пользовательский редактор не требуется!
- Обновить
amt
колонка наqty
или жеprice
Обновление столбцов: это также можно сделать при работе с табличной моделью, просто нужно правильно переопределить setValueAt (...). - дисплей
price
а такжеamt
столбцы с форматом валюты: это можно сделать с помощью настраиваемого средства визуализации (не редактора).
Поскольку информация о вашей табличной модели отсутствует, я проиллюстрирую вышеприведенные пункты, используя DefaultTableModel, следующим образом:
String[] header = new String[] { "qty", "price", "amt" };
DefaultTableModel model = new DefaultTableModel(header, 1) {
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0: return Integer.class;
case 1:
case 2: return Double.class;
}
throw new ArrayIndexOutOfBoundsException(columnIndex);
}
@Override
public boolean isCellEditable(int row, int column) {
return column < 2;
}
@Override
public void setValueAt(Object aValue, int row, int column) {
super.setValueAt(aValue, row, column);
if (column < 2) {
Integer qty = (Integer)getValueAt(row, 0);
Double price = (Double)getValueAt(row, 1);
Double amt = (qty != null && price != null)
? (Double)(qty * price)
: null;
super.setValueAt(amt, row, 2);
}
}
};
Некоторые заметки о примере:
- В этом примере я установил только три столбца, которые действительно имеют значение:
qty
,price
а такжеamt
, - Только
qty
а такжеprice
столбцы доступны для редактирования в то время какamt
не редактируется (он рассчитывается при обновлении других столбцов). - Учитывая
getColumnClass()
В соответствии с реализацией редактор по умолчанию не допустит неправильные входные данные ни для одного из числовых столбцов: независимо от того, является ли класс столбца целочисленным, он допускает только целочисленные значения. То же самое относится к классу Double. - Если либо
qty
или жеprice
столбец изменен тогдаsetValueAt(...)
вызывается иamt
колонка также обновляется соответственно.
Наконец, чтобы применить формат валюты, когда ячейка отображается (не редактируется), нам нужно предоставить пользовательский рендер. Например:
class CurrencyRenderer extends DefaultTableCellRenderer {
private final NumberFormat currencyFormat;
public CurrencyRenderer() {
currencyFormat = NumberFormat.getCurrencyInstance();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof Number) {
setText(currencyFormat.format((Number)value));
}
return renderer;
}
}
Теперь, есть разные способы предоставления рендерера, все объяснено здесь: Понятия: редакторы и рендеры