Un modo efficiente per visualizzare un contatore del tempo in una cella JTable
Domanda
Un contatore del tempo mostra l'età in secondi di una riga nella tabella. Idealmente, sarebbe aggiornato una volta al secondo. So di poter semplicemente incrementare i dati appropriati nel modello di tabella, attivare gli eventi (uno per riga), ecc. Sembra eccessivo! C'è un modo migliore e più leggero?
Soluzione
Ciò di cui hai bisogno è:
- un modo per modellare l '"età" della riga nel modello di tabella. Questa operazione viene eseguita in modo implicito, quindi è sufficiente memorizzare il tempo di creazione della riga una volta e calcolare l'età in cui viene richiesto il valore della cella (Object getValueAt (riga, colonna))
- Un thread (daemon) che genera l'evento di cambio tabella ogni secondo. Non è necessario generare un evento per riga, ma è possibile generare un evento che segnala un aggiornamento di un'intera colonna.
Ecco alcuni pseudocodici per il modello di tabella:
public Object getValueAt (int rowIndex, int columnIndex) {
// if it's the column with the 'row age', calculate the age and return it
long rowAgeMs = System.currentTimeMs() - getCreationTime(rowIndex);
// return the age in milliseconds, or a date, or a formatted time string
}
Il modello di tabella dovrebbe quindi offrire anche un metodo per il thread, in modo che possa generare un evento change per la colonna 'row age':
MyTableModel di classe pubblica implementa TableModel {
private final List<TableModelListener> listeners = new LinkedList<TableModelListener>();
public void addTableModelListener (TableModelListener l) {
listeners.add(l);
}
public void removeTableModelListener (TableModelListener l) {
listeners.remove(l);
}
public void updateColumn (int column) {
TableModelEvent evt = new TableModelEvent(this, 0, Math.max(0, getRowCount() - 1), column);
for (TableModelListener listener : listeners) {
listener.tableChanged(evt);
}
}
Il thread attiverà quindi il metodo updateColumn (..) ogni secondo per la colonna 'row row'. L'invocazione di questo metodo dovrebbe essere effettuata in EventDispatchThread, questo avviene usando SwingUtilities.invokeAndWait (..) o SwingUtilities.invokeLater (..).
Thread rowAgeUpdater = new Thread() {
@Override
public void run () {
while (isAlive()) {
try {
long time = System.currentTimeMillis();
long sleepTime = (time / 1000 + 1) * 1000 - time;
Thread.sleep(sleepTime);
SwingUtilities.invokeAndWait(new Runnable() {
public void run () {
model.updateColumn(ROW_AGE_COLUMN_INDEX);
}
});
} catch (Exception e) {
return;
}
}
}
};
rowAgeUpdater.setDaemon(true);
rowAgeUpdater.setPriority(Thread.MIN_PRIORITY);
rowAgeUpdater.start();
Finché la granularità di TableModelEvent copre solo le celle che devono essere aggiornate (nel tuo caso: solo la colonna con l'età della riga), è il modo più efficiente per realizzarlo.