2012-03-02 13 views
5

Breve descrizione di un problema.Interagire con JTable che viene rapidamente aggiornato con nuove righe

Supponiamo di avere JTable e l'utente che interagiscono con esso in qualche modo. TableModel di questa tabella è in continua evoluzione. Come assicurarsi che quando l'utente tenta di ottenere alcune informazioni dalla tabella facendo riferimento a una colonna costante e alla riga attualmente selezionata (dal suo rowIndex ricevuto da JTable), non entrerà in situazione quando TableModel viene modificato e il suo rowIndex ottenuto da JTable è non corrisponde più allo stesso valore in TableModel.

seguito è domanda iniziale che spiega problema in modo più dettagliato:

Considerate seguente situazione:

  1. C'è JTable che mostra informazioni utente sulle richieste attualmente in esecuzione in un qualche sistema

  2. Quando una nuova richiesta entra nel sistema, viene aggiunta una nuova riga alla tabella

  3. utente può interagire con la tabella facendo clic destro su un (modello di singola riga di selezione è utilizzato in tabella) di fila e l'opzione scegliendo dal menu (come: annullare, rinviare, riprovare, ecc)

  4. v'è separata classe che implementa l'interfaccia ActionListener (ascolta la tabella) e gestisce tutte le interazioni dell'utente

  5. Quando l'utente esegue un'azione sulla tabella, questa classe controlla la riga attualmente selezionata e assegna alcuni valori all'azione dell'utente (in pratica, prende l'indice della riga selezionata quindi chiama tableModel.getValueAt (indexOfSelectedRow, someValuableDataColumnIndex))

Considerare ora lo scenario quando il sistema è sottoposto a stress test e le richieste vengono inviate costantemente con grande frequenza. Questo, nel mio caso, porta a un bug, quando a volte la classe che gestisce le azioni dell'utente riceve informazioni errate dal modello di tabella (l'azione è stata chiamata su una riga, ma l'azione viene eseguita per un'altra, di solito la prossima). Credo che ciò accada perché durante alcune fasi della gestione delle azioni il modello di tabella delle classi viene modificato a causa della nuova richiesta accettata.

La domanda è, come risolvere questo problema. Sto pensando di due approcci:

  • uso qualcosa di simile invokeAndWait() per l'inizializzazione in classe movimentazione mie azioni degli utenti (non piace questa idea, perché imo porterà ad altri bug imprevedibili)

  • creazione di una classe listener separata che ascolterà le selezioni dell'utente nella tabella e memorizzerà i dati dalla riga selezionata non appena è stata selezionata separatamente da TableModel. In questo modo le azioni di gestione delle classi prenderanno i dati non dal modello di tabella che viene modificato, ma dalla riga selezionata, che è costante durante lo scenario descritto. (non sono sicuro che questa idea funzioni)

Per favore, commenta le mie idee e suggerisci le tue.

Mi dispiace per l'assenza di qualsiasi codice qui, ma il codice originale occuperà troppo spazio e l'esempio di modello non è qualcosa che può essere fatto facilmente qui.

+0

Dopo averci pensato un po 'per la mia seconda idea mi sono reso conto che non funziona. Ha gli stessi due passaggi: 1) ottenere l'indice di riga selezionato 2) ottenere informazioni dal modello di tabella per una riga con tale indice. Ogni nuova riga che appare in una tabella tra questi due passaggi rovinerà tutto. – GrayR

+3

Quanti thread accedono a 'TableModel'? Se più di uno, come sincronizzano l'accesso? Vedi anche questo [sscce] (http://stackoverflow.com/a/7519403/230513) e questo [Q & A] (http://stackoverflow.com/q/7787998/230513). – trashgod

+0

@trashgod TableModel è accessibile da un solo thread. Grazie anche per i collegamenti, informazioni interessanti. Non riesco a capire nulla di utile per ora, ma sicuramente lo esamineremo più profondamente. – GrayR

risposta

4

Non credo che l'inserimento di righe in una tabella cambi la selezione, quindi finché si aggiorna il TableModel sull'EDT, la riga selezionata è sempre la stessa quando l'utente mostra il popup e sceglie l'azione dal popup .

import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 

import javax.swing.*; 
import javax.swing.table.DefaultTableModel; 

public class TestJTableInsert { 
    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       final DefaultTableModel model = new DefaultTableModel(0, 1); 
       new Timer(500, new ActionListener() { 
        private final Random random = new Random(); 
        private int data = 1; 

        @Override 
        public void actionPerformed(ActionEvent e) { 
         model.insertRow(random.nextInt(model.getRowCount() + 1), 
           new Object[] { data++ }); 
        } 
       }).start(); 

       final JTable table = new JTable(model); 
       JPopupMenu popup = new JPopupMenu(); 
       popup.add(new AbstractAction("Which row is this?") { 

        @Override 
        public void actionPerformed(ActionEvent e) { 
         JOptionPane.showMessageDialog(table, 
           "This is row " + table.getValueAt(table.getSelectedRow(), 0)); 
        } 
       }); 
       table.setComponentPopupMenu(popup); 
       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
       frame.getContentPane().add(new JScrollPane(table)); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

maledizione, mi hai battuto - perché dovevo provare di essere umano (leggendo un captcha quasi indecifrabile :) – kleopatra

3

esempio modella non è qualcosa di ciò che può essere fatto facilmente qui.

Ecco un antipasto per dimostrare che la selezione è mantenuta costante (nel senso che punta sempre alla stessa riga "reale":

final DefaultTableModel model = new DefaultTableModel(0, 1); 
for (int i = 0; i < 50; i++) { 
    model.addRow(new Object[] {i}); 
}; 

final JXTable table = new JXTable(model); 
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 
table.setRowSelectionInterval(25, 25); 
Action l = new AbstractAction("random insert") { 
    Random random = new Random(); 
    @Override 
    public void actionPerformed(ActionEvent e) { 
     int row = random.nextInt(model.getRowCount()); 
     model.insertRow(row, new Object[] {"inserted at: " + row}); 
     table.scrollRowToVisible(table.getSelectedRow()); 
    } 

}; 
new Timer(100, l).start();