2012-03-02 11 views
5

Breve descripción de un problema.Interactuando con JTable que se actualiza rápidamente con filas nuevas

Supongamos que JTable y el usuario interactúan con él de alguna manera. TableModel de esta tabla cambia constantemente. Cómo garantizar que cuando el usuario intente obtener información de la tabla al hacer referencia a una columna constante y la fila actualmente seleccionada (por su rowIndex que obtuvo de JTable), no entrará en una situación cuando TableModel se modifique y su rowIndex obtenido de JTable sea ya no corresponde al mismo valor en TableModel.

A continuación se pregunta inicial que explica problema con más detalle:

Considere siguiente situación:

  1. No es JTable que muestra información del usuario acerca de que se están ejecutando las solicitudes en algún sistema

  2. Cuando la nueva solicitud ingresa al sistema, se agrega una nueva fila a la tabla

  3. usuario puede interactuar con la tabla haciendo clic derecho en una fila (modelo de una sola fila de selección se utiliza en la tabla) y la opción de elegir del menú (como: abortar, posponer, reintentar, etc.)

  4. existe separada clase que implementa la interfaz ActionListener (escucha la tabla) y maneja todas las interacciones del usuario

  5. Cuando el usuario realiza alguna acción en la tabla, esta clase verifica la fila seleccionada actualmente y asigna algunos valores para la acción del usuario (básicamente toma el índice de la fila seleccionada y luego llama a tableModel.getValueAt (indexOfSelectedRow, someValuableDataColumnIndex))

Considere ahora el escenario cuando el sistema está en prueba de esfuerzo y las solicitudes se envían constantemente con gran frecuencia. Esto, en mi caso, conduce a un error, cuando a veces la clase que maneja las acciones del usuario obtiene información incorrecta del modelo de la tabla (se invocó una acción en una fila, pero la acción se realiza para otra, generalmente la siguiente). Creo que esto sucede porque durante algunos pasos en la acción, el modelo de tabla de clase de manejo se cambia debido a la nueva solicitud aceptada.

La pregunta es, cómo solucionar esto. Estoy pensando en dos enfoques:

  • usar algo como invokeAndWait() para la inicialización en la clase de manejo de mis acciones de usuario (no me gusta esta idea, debido a la OMI que dará lugar a otros errores impredecibles)

  • creando una clase de escucha separada que escuchará las selecciones de los usuarios en la tabla y almacenará los datos de la fila seleccionada tan pronto como se haya seleccionado por separado de TableModel. De esta forma, las acciones que manejan clases tomarán datos no del modelo de tabla que se está modificando, sino de la fila seleccionada, que es constante durante el escenario descrito. (no estoy seguro de que esta idea funcione)

Por favor, comenta mis ideas y sugiere las tuyas.

Lamento la ausencia de código aquí, pero el código original ocupará demasiado espacio, y el ejemplo del modelo no es algo que se pueda hacer fácilmente aquí.

+0

Después de pensar un poco acerca de mi segunda idea me di cuenta que no funcionará. Tiene los mismos dos pasos: 1) obtener el índice de fila seleccionado 2) obtener información del modelo de tabla para una fila con ese índice. Cualquier nueva fila que aparezca en la tabla en el medio de esos dos pasos arruinará las cosas. – GrayR

+3

¿Cuántos subprocesos tienen acceso al 'TableModel'? Si hay más de uno, ¿cómo sincronizan el acceso? Ver también esto [sscce] (http://stackoverflow.com/a/7519403/230513) y este [Q & A] (http://stackoverflow.com/q/7787998/230513). – trashgod

+0

@trashgod Se accede a TableModel por un solo hilo. También gracias por los enlaces, información interesante. No se puede encontrar nada útil a partir de ahora, pero seguramente lo investigará más a fondo. – GrayR

Respuesta

4

No creo que la inserción de filas en una tabla cambie la selección, de modo que mientras esté actualizando TableModel en el EDT, la fila seleccionada seguirá siendo la misma cuando el usuario muestre la ventana emergente y elija una acción de la ventana emergente .

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

maldita sea, me ganaste - porque tenía que probar ser humano (leyendo un captcha casi indecifrable :) – kleopatra

3

ejemplo modelo no es algo que se puede hacer fácilmente aquí.

Aquí hay un plato para demostrar que la selección se mantiene constante (en el sentido de que siempre apunta a la misma "real" consecutivas:

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(); 
Cuestiones relacionadas