tags:

views:

213

answers:

1

A time counter shows the age in seconds of a row in the table. Ideally, it would be updated once per second. I know I can just increment the appropriate data in the table model, fire the events (one per row), etc. It seems like overkill. Is there a better, lighter way?

+1  A: 

What you need is:

  • a way to model the 'age' of the row in your table model. This is best done implicitely, so you just store the creation time of the row once and calculate the age when the cell value is requested (Object getValueAt(row, column)).
  • A (daemon) thread which fires the table change event each second. You don't have to fire one event per row, but instead you can fire an event that signals a whole column update.

Here's some pseudocode for the table model:

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 
}

The table model should then also offer a method for the thread, so it can fire a change event for the 'row age' column:

public class MyTableModel implements 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);
          }
   }

The thread would then just trigger the updateColumn(..) method each second for the 'row age' column. The invocation of this method should be done in the EventDispatchThread, this is done using SwingUtilities.invokeAndWait(..) or 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();

As long as the granularity of the TableModelEvent only covers the cells that need to be updated (in your case: only the column with the row age), it's the most efficient way to realize this.

Peter Walser