2011-11-11 3 views
5

ho inviato una risposta a Java TableModelListener and Live Feed Listener?, ma ottengo un commento di - KleopatraPerché mai cambiare il notificante a ricevere un evento di modifica

nonono - you never change the notifier in receiving a change event. 
As to probable effects, think: nasty loops. As to code sanity, think: 
indecent intimacy. It's the task of the model itself to internally 
update related values if necessary. 

può somone per spiegare me che cosa cambiamo il notificante a ricevere un evento di modifica, quello che potrebbe essere accaduto, che cosa significa veramente, perché, come ho provato tutto quello che so che ho solo ricevere eccezioni RepaintManager dal ciclo molto rapidamente,

ho mai un'altra eccezione, dove

  • I multiplaeyd che alla matrice 50 x 1000,

  • con prepareRenderer (cambiamento di colore per il valore possitive/negativo)

  • con frequenza di aggiornamento 175 millisecondi

codice dimostrato cambiando la notificatore e due altri (forse corretto) modo come fare quello

import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.util.Random; 
import java.util.concurrent.*; 
import javax.swing.*; 
import javax.swing.event.*; 
import javax.swing.table.DefaultTableModel; 

public class ChangeNotifiersOnEvent extends JFrame implements Runnable { 

    private static final long serialVersionUID = 1L; 
    private boolean runProcess = true; 
    private Random random = new Random(); 
    private javax.swing.Timer timerRun; 
    private Executor executor = Executors.newCachedThreadPool(); 
    private String[] columnNames = {"Source", "Hit", "Last", "Ur_Diff"}; 
    private JTable table; 
    private Object[][] data = {{"Swing Timer", 2.99, 5, 1.01}, 
     {"Swing Worker", 7.10, 5, 1.010}, {"TableModelListener", 25.05, 5, 1.01}}; 
    private DefaultTableModel model = new DefaultTableModel(data, columnNames); 

    public ChangeNotifiersOnEvent() { 
     table = new JTable(model) { 

      private static final long serialVersionUID = 1L; 

      @Override 
      public Class getColumnClass(int column) { 
       return getValueAt(0, column).getClass(); 
      } 
     }; 
     model.addTableModelListener(new TableModelListener() { 

      @Override 
      public void tableChanged(TableModelEvent tme) { 
       if (tme.getType() == TableModelEvent.UPDATE) { 
        if (tme.getColumn() == 1 && tme.getLastRow() == 2) { 
         double dbl = ((Double) table.getModel().getValueAt(2, 1)) 
           - ((Integer) table.getModel().getValueAt(2, 2)); 
         table.getModel().setValueAt(dbl, 2, 3); 
        } else if (tme.getColumn() == 1 && tme.getLastRow() == 0) { 
         prepareUpdateTableCell(); 
        } else if (tme.getColumn() == 1 && tme.getLastRow() == 1) { 
         executor.execute(new MyTask(MyTask.UPDATE_TABLE_COLUMN)); 
        } 
       } 
      } 
     }); 
     table.setRowHeight(30); 
     table.setFont(new Font("Serif", Font.BOLD, 20)); 
     table.getColumnModel().getColumn(0).setPreferredWidth(180); 
     table.setPreferredScrollableViewportSize(table.getPreferredSize()); 
     JScrollPane scrollPane = new JScrollPane(table); 
     add(scrollPane, BorderLayout.CENTER); 
     new Thread(this).start(); 
    } 

    private void prepareUpdateTableCell() { 
     timerRun = new javax.swing.Timer(10, UpdateTableCell()); 
     timerRun.setRepeats(false); 
     timerRun.start(); 
    } 

    private Action UpdateTableCell() { 
     return new AbstractAction("Update Table Cell") { 

      private static final long serialVersionUID = 1L; 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       double dbl = ((Double) table.getModel().getValueAt(0, 1)) 
         - ((Integer) table.getModel().getValueAt(0, 2)); 
       table.getModel().setValueAt(dbl, 0, 3); 
      } 
     }; 
    } 

    @Override 
    public void run() { 
     while (runProcess) { 
      try { 
       Thread.sleep(250); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
      changeTableValues(); 
     } 
    } 

    private void changeTableValues() { 
     Runnable doRun = new Runnable() { 

      @Override 
      public void run() { 
       table.getModel().setValueAt(random.nextInt(128) + random.nextDouble(), 0, 1); 
       table.getModel().setValueAt(random.nextInt(256) + random.nextDouble(), 1, 1); 
       table.getModel().setValueAt(random.nextInt(512) + random.nextDouble(), 2, 1); 

       table.getModel().setValueAt(random.nextInt(128), 0, 2); 
       table.getModel().setValueAt(random.nextInt(128), 1, 2); 
       table.getModel().setValueAt(random.nextInt(128), 2, 2); 
      } 
     }; 
     SwingUtilities.invokeLater(doRun); 
    } 

    private class MyTask extends SwingWorker<Void, Integer> { 

     private static final String UPDATE_TABLE_COLUMN = "update"; 
     private String namePr; 
     private double dbl; 

     MyTask(String str) { 
      this.namePr = str; 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      dbl = ((Double) table.getModel().getValueAt(1, 1)) 
        - ((Integer) table.getModel().getValueAt(1, 2)); 
      return null; 
     } 

     @Override 
     protected void done() { 
      SwingUtilities.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        table.getModel().setValueAt(dbl, 1, 3); 
       } 
      }); 
     } 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       ChangeNotifiersOnEvent frame = new ChangeNotifiersOnEvent(); 
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 
       frame.setLocation(150, 150); 
       frame.pack(); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

Perché l'implementazione MyTask.done() usa invokeLater? Se hai bisogno di fare qualcosa sull'EDT fatto() è la tua occasione. – Ryan

+0

@Ryan code è a scopo di test, utilizzo di invokeLater ---> a) notifica EDT, b) spostamento del codice desiderato alla fine di EDT (molte volte menzionate per JTextComponents e i suoi XxxListeners, Focus ecc ...), sicuramente implementazioni in API per done, process, publish, setProcess è abbastanza sicuro che tutti gli eventi sono fatti su EDT, sry I'm Ote SwingWokrer – mKorbel

+0

Un metodo SwingWorker.done() che fa invocare solo un invokeLater come un anti-pattern. Capisco "test b/c". Non credo ancora a) ob) realizzare nulla in questo esempio. Se stai cercando di capire qualcosa che non ha bisogno di threading inutili non aiuta. – Ryan

risposta

5

Penso significa che se non pensi davvero al tuo codice, allora puoi introdurre un ciclo infinito.

La maggior parte delle persone quando creano la tabella probabilmente rendono modificabili le colonne 1, 2 e rendono non modificabile la colonna 3 poiché la colonna 3 rappresenta solo la differenza tra le due colonne.

Così quando scrivono il TableModelListener controlleranno per l'evento UPDATE ma dimentica di controllare per vedere quale colonna viene aggiornato perché pensano che il tavolo non permetterà loro di aggiornare colonna 3.

Essi dimenticano che quando il TableModelListener aggiorna la colonna 3 generando un altro evento UPDATE, causando così il ciclo infinito. Ovviamente, la corretta codifica, come nel tuo esempio, impedirà il loop.

In generale, non dovrebbe causare un'eccezione.

Il secondo punto riguarda le regole aziendali. Le regole aziendali dovrebbero essere definite in un unico posto, in questo caso il modello. I dati stessi e l'aggiornamento dei dati dovrebbero essere fatti in un unico posto.

+1

grazie per chiarire, +1 – mKorbel

Problemi correlati