Обновление JTable в Swing дает исключение - PullRequest
3 голосов
/ 09 мая 2011

Таким образом, я пытаюсь обновить мой JTable каждый раз, когда нажимается клавиша, или когда происходит обновление со стороны наблюдателя. Изредка получаю замороженное приложение и трассировку стека ...

Исключение в потоке "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 5> = 5 at java.util.Vector.elementAt (неизвестный источник) в javax.swing.table.DefaultTableColumnModel.getColumn (неизвестный источник) в javax.swing.plaf.synth.SynthTableUI.paintCells (Неизвестный источник) в javax.swing.plaf.synth.SynthTableUI.paint (Неизвестный источник) at javax.swing.plaf.synth.SynthTableUI.update (неизвестный источник) на javax.swing.JComponent.paintComponent (неизвестный источник) на javax.swing.JComponent.paint (неизвестный источник) на javax.swing.JComponent.paintToOffscreen (неизвестный источник) в javax.swing.RepaintManager $ PaintManager.paintDoubleBuffered (неизвестный источник) на javax.swing.RepaintManager $ PaintManager.paint (неизвестный источник) на javax.swing.RepaintManager.paint (неизвестный источник) на javax.swing.JComponent._paintImmediately (неизвестный источник) на javax.swing.JComponent.paintImmediately (неизвестный источник) на javax.swing.RepaintManager.paintDirtyRegions (неизвестный источник) на javax.swing.RepaintManager.paintDirtyRegions (неизвестный источник) в javax.swing.RepaintManager.seqPaintDirtyRegions (неизвестный источник) в javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run (неизвестный источник) в java.awt.event.InvocationEvent.dispatch (Неизвестный источник) в java.awt.EventQueue.dispatchEvent (Неизвестный источник) в java.awt.EventDispatchThread.pumpOneEventForFilters (Неизвестный источник) в java.awt.EventDispatchThread.pumpEventsForFilter (Неизвестный источник) в java.awt.EventDispatchThread.pumpEventsForHierarchy (Неизвестный источник) в java.awt.EventDispatchThread.pumpEvents (Неизвестный источник) в java.awt.EventDispatchThread.pumpEvents (Неизвестный источник) at java.awt.EventDispatchThread.run (неизвестный источник) Исключение в потоке "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 4> = 4 at java.util.Vector.elementAt (неизвестный источник) в javax.swing.table.DefaultTableColumnModel.getColumn (неизвестный источник) at sun.swing.SwingUtilities2.convertColumnIndexToModel (неизвестный источник) в javax.swing.JTable.convertColumnIndexToModel (Неизвестный источник) на javax.swing.JTable.getColumnClass (неизвестный источник) at javax.swing.plaf.synth.SynthTableUI $ SynthTableCellRenderer.getTableCellRendererComponent (Неизвестный источник) в javax.swing.JTable.prepareRenderer (Неизвестный источник) на javax.swing.plaf.synth.SynthTableUI.paintCell (неизвестный источник) в javax.swing.plaf.synth.SynthTableUI.paintCells (Неизвестный источник) в javax.swing.plaf.synth.SynthTableUI.paint (Неизвестный источник) at javax.swing.plaf.synth.SynthTableUI.update (неизвестный источник) на javax.swing.JComponent.paintComponent (неизвестный источник) на javax.swing.JComponent.paint (неизвестный источник) на javax.swing.JComponent.paintToOffscreen (неизвестный источник) в javax.swing.RepaintManager $ PaintManager.paintDoubleBuffered (неизвестный источник) на javax.swing.RepaintManager $ PaintManager.paint (неизвестный источник) на javax.swing.RepaintManager.paint (неизвестный источник) на javax.swing.JComponent._paintImmediately (неизвестный источник) на javax.swing.JComponent.paintImmediately (неизвестный источник) на javax.swing.RepaintManager.paintDirtyRegions (неизвестный источник) на javax.swing.RepaintManager.paintDirtyRegions (неизвестный источник) в javax.swing.RepaintManager.seqPaintDirtyRegions (неизвестный источник) в javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run (неизвестный источник) в java.awt.event.InvocationEvent.dispatch (Неизвестный источник) в java.awt.EventQueue.dispatchEvent (Неизвестный источник) в java.awt.EventDispatchThread.pumpOneEventForFilters (Неизвестный источник)в java.awt.EventDispatchThread.pumpEventsForFilter (неизвестный источник) в java.awt.EventDispatchThread.pumpEventsForHierarchy (неизвестный источник) в java.awt.EventDispatchThread.pumpEvents (неизвестный источник) в java.awtEvent.awt.EventDispatchThread.run (Неизвестный источник)

Если это поможет, вот мой код для обновления таблицы:

public synchronized void refreshTable()
{
    Customer cust = custManager.getCustomer(phoneNumber.getText());

    if (cust == null)
    {
        table.setModel(new DefaultTableModel(new Object[][] {}, tableHeader) {
                @SuppressWarnings("rawtypes")
                Class[] columnTypes = new Class[] {Integer.class, String.class,
                                                   Object.class, Object.class, 
                                                   Object.class, Object.class};
                @SuppressWarnings({ "unchecked", "rawtypes" })
                public Class getColumnClass(int columnIndex) {
                    return columnTypes[columnIndex];
                }

                public boolean isCellEditable(int row, int column) {
                    return false;
                }
        });
        table.getColumnModel().getColumn(0).setPreferredWidth(40);
        table.getColumnModel().getColumn(1).setPreferredWidth(120);
        return;
    }

    Object[][] grid = new Object[cust.getOrderHistory().size()][6];

    SimpleDateFormat sdf = new SimpleDateFormat("MMM/dd/yyyy HH:mm");
    NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();

    int i = 0;
    for (Entry<GregorianCalendar, Order> orderEntry : cust.getOrderHistory())
    {
        Order order = orderEntry.getValue();

        grid[i][0] = order.getOrderID();
        grid[i][1] = sdf.format((order.getProcessedTimestamp().getTime()));
        grid[i][2] = currencyFormat.format(order.getSubTotal()/100.00);
        grid[i][3] = currencyFormat.format(order.getTaxedAmount()/100.00);
        grid[i][4] = currencyFormat.format(order.getTotal()/100.00);
        grid[i][5] = order.getOrderStatus();

        i++;
    }

    DefaultTableModel dft = new DefaultTableModel(grid, tableHeader) {
            @SuppressWarnings("rawtypes")
            Class[] columnTypes = new Class[] {Integer.class, String.class, 
                                               Object.class, Object.class, 
                                               Object.class, Object.class};
            @SuppressWarnings({ "unchecked", "rawtypes" })
            public Class getColumnClass(int columnIndex) {
                return columnTypes[columnIndex];
            }

            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };

    table.setModel(dft);
    table.getColumnModel().getColumn(0).setPreferredWidth(40);
    table.getColumnModel().getColumn(1).setPreferredWidth(120);
}

Для записи я попытался обернуть еевокруг в попытке / поймать, и нет, это не поможет.Я также попытался изменить обработчик исключений по умолчанию, но это также оказалось неудачным.

Ответы [ 3 ]

3 голосов
/ 09 мая 2011

Одна из самых больших проблем, которую я вижу, это то, что вы не привязываете таблицу к своим данным, а затем обновляете ячейки по мере их изменения, вы меняете всю модель данных из-под таблицы каждый раз, когда нажимаете клавишу.

Я бы рискнул предположить, что у вас "расовое" состояние. Вы нажимаете клавишу, и это начинает процесс перестройки вашего стола и его модели. Пройдя через эту перестройку (помните, что Swing использует потоки, поэтому обновление таблицы все еще происходит , пока вы печатаете,) вы запускаете и запускаете другую перестройку, которая меняет модель из-под первого обновления который все еще работает . Конечно, не видя весь ваш код, я не могу быть в этом уверен, но могу предложить несколько вещей, которые сделают вещи чище и менее подверженными проблемам параллелизма.

Вместо того, чтобы писать код, который берет данные вашей модели и переводит их в простую модель таблицы при каждом нажатии клавиши, вы можете создать свой собственный класс модели таблицы и реализовать его для непосредственного просмотра вашей модели. Таким образом, у вас не будет проблемы повторной передачи данных между вашей моделью и массивом.

Чтобы создать собственную модель таблицы, вы можете просто расширить ее из AbstractTableModel (http://download.oracle.com/javase/6/docs/api/javax/swing/table/AbstractTableModel.html). В оригинальном Java Tutorial есть отличный пример (http://download.oracle.com/javase/tutorial/uiswing/components/table.html#data), если вы не знаете, как это сделать. Самым важным для вас является метод getValueAt, который берет строку и столбец и возвращает значение, которое должна содержать эта ячейка. Именно туда вы хотите поместить код, который просматривает ваш объект «Порядок» и выдает желаемый результат.

Когда у вас есть свой пользовательский объект TableModel, просто передайте его новый экземпляр в метод "setModel" таблицы при первой его инициализации.

Как только это будет сделано, вы захотите обработать разумные обновления модели. Чтобы обновить все данные в таблице, вы можете вызвать событие «fireTableDataChanged». Кроме того, вы можете обновлять только те части таблицы, которые нужно изменить, а не все время. Вместо того, чтобы вдаваться в мелкие детали здесь, я просто укажу вам на отличную страницу Учебника Java, посвященную именно этому: http://download.oracle.com/javase/tutorial/uiswing/components/table.html#fire

2 голосов
/ 09 мая 2011

Как уже подчеркивалось в других ответах: замораживание является типичным признаком того, что вы вызываете refreshTable из потока, который не является EDT - общее правило состоит в том, что любой доступ к сотрудникам Swing, будь то представление или модель , должен случиться на EDT.

Самый простой способ (только для начала) добиться этого - вызывать каждый вызов refreshTable

  public void invokeRefreshTable(final OrderTableModel model) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            refreshTable(model)
        }
    }); 

  }

На самом деле, вы, вероятно, не хотите обновлять таблицу , вам нужно обновить модель , сохранив конфигурацию таблицы как есть - не нужно переконфигурировать ширина столбца. (Также уже упоминалось) ваш фрагмент кода выглядит так, как будто вы могли бы извлечь выгоду из пользовательской реализации tableModel: это список заказов. Для этого нужно украсить этот список с помощью TableModel, например:

public static class OrderTableModel extends AbstractTableModel {
    List<Order> orders = new ArrayList<Order>();
    Class<?>[] columnTypes = new Class[] { Integer.class,
            Timestamp.class, BigDecimal.class, BigDecimal.class, BigDecimal.class,
            Object.class };

    public void setOrders(Customer customer) {
        setOrders(customer != null ? customer.getOrderHistory() : null);
    }

    public void setOrders(Map<?, ?> orderHistory) {
        clear();
        if (orderHistory == null) return;
        for (Entry<?, ?> orderEntry: orderHistory) {
            orders.add(orderEntry.getValue());
        }
        fireTableRowsInserted(0, orders.size() - 1);
    }

    public void clear() {
        int rowCount = orders.size();
        orders.clear();
        if (rowCount > 0 ) {
            fireTableRowsDeleted(0, rowCount -1);
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
       switch (columnIndex) {
           case 0: return orders.get(rowIndex).getOrderID();
           //....

           default:
               break;
       }

        return null;
    }

    @Override
    public int getRowCount() {
        return orders.size();
    }

    @Override
    public int getColumnCount() {
        return columnTypes.length;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnTypes[columnIndex];
    }

}

// the single model instance to re-use always
OrderTableModel model = new OrderTableModel()
// separate initial configuration of the table
JTable table = new JTable(model);
// register custom renderers
table.setDefaultRenderer(BigDecimal.class, new BigDecimalRenderer());
table.setDefaultRenderer(Timestamp.class, new TimeStampRenderer());
// config the table
table.getColumnModel().getColumn(0).setPreferredSize(...)
....

// the refresh method crumpled down to a single line
public void refreshTable(OrderTableModel model) {
    model.setCustomer(customManager.getCustomer(phoneNumber))
}

// if refreshing is needed
invokeRefreshTable(model);

Примечание:

  • модель содержит объекты real и ссылки на их свойства real : форматирование является задачей cellRenderers
  • модель может иметь столько связанных с доменом методов, сколько это полезно: здесь она может повторно заполнить себя либо из экземпляра Customer, либо из его OrderHistory
2 голосов
/ 09 мая 2011

Синхронное? Я боюсь, что вы делаете свинг-звонки из EDT. Вы позаботились о том, чтобы не нарушить правило однопоточности Swing?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...