Пользовательские ячейки в 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);
        }
    }
};

Некоторые заметки о примере:

  1. В этом примере я установил только три столбца, которые действительно имеют значение: qty, price а также amt,
  2. Только qty а также price столбцы доступны для редактирования в то время как amt не редактируется (он рассчитывается при обновлении других столбцов).
  3. Учитывая getColumnClass() В соответствии с реализацией редактор по умолчанию не допустит неправильные входные данные ни для одного из числовых столбцов: независимо от того, является ли класс столбца целочисленным, он допускает только целочисленные значения. То же самое относится к классу Double.
  4. Если либо 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;
    }
}

Теперь, есть разные способы предоставления рендерера, все объяснено здесь: Понятия: редакторы и рендеры

Другие вопросы по тегам