Adding a JProgressBar inside a JTable cell

A JTable registers default renderers for a Number, Date, ImageIcon, Boolean and Object. If you want to render another component, you have to create a custom class that implements TableCellRenderer and register it with the method setDefaultRenderer on the JTable object.

The following example creates a JTable where every row displays data from a Download object. A download object is a thread that mimics the behavior of downloading a file by increasing the number of downloaded bytes (progress) with a random value. Using the Observable-Observer pattern, the download object notifies the table model whenever the progress of downloading has changed.

Main.java:

import javax.swing.table.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
 
public class Main extends JFrame {
   public Main() {
      super("TableModel JProgressBar Demonstration");
 
      // create our own custom TableModel
      DownloadTableModel downloadModel = new DownloadTableModel();
      JTable table = new JTable(downloadModel);
 
      // add rows to our TableModel, each row is represented as a Download object
      downloadModel.addDownload(new Download("linuxmandrake.zip", 1234567));
      downloadModel.addDownload(new Download("flash5.exe", 56450000));
      downloadModel.addDownload(new Download("jdk1.2.2-007.zip", 20000000));
 
      // render the columns with class JProgressBar as such
      ProgressBarRenderer pbr = new ProgressBarRenderer(0, 100);
      pbr.setStringPainted(true);
      table.setDefaultRenderer(JProgressBar.class, pbr);
   
      // increase the height of the rows a bit
      table.setRowHeight((int) pbr.getPreferredSize().getHeight());
 
      // create the scroll pane and add the table to it. 
      JScrollPane scrollPane = new JScrollPane(table);
 
      // add the scroll pane to this window.
      getContentPane().add(scrollPane, BorderLayout.CENTER);
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   public static void main(String[] args) {
      Main main = new Main();
      main.pack();
      main.setVisible(true);
   }
}
 
// a simple object that holds data about a particular download
// it starts a thread and increases the progress of "downloading"
// in a random manner
class Download extends Observable implements Runnable {
   private Thread  thisThread;
 
   private String  filename;
   private int     filesize;
   private float   progress;
 
   public Download(String filename, int filesize) {
      this.filename = filename;
      this.filesize = filesize;
      progress = 0.0f;
      thisThread = new Thread(this);
      thisThread.start();
   }
 
   public String getFilename() { return filename; }
   public int    getFilesize() { return filesize; }
   public float  getProgress() { return progress; }
 
   public String toString() { 
      return "[" + filename + ", " + filesize + ", " + progress + "]"; }

   public void run() {
      Random r = new Random();
      int count = 0;
      while (count < filesize) {
         int random = Math.abs(r.nextInt() % 100000);
         count += random;
         if (count > filesize) count = filesize; 
         progress = ((float) count / filesize) * 100;
 
         // notify table model (and all other observers)
         setChanged();
         notifyObservers(this);       
 
         try { thisThread.sleep(500); } catch(InterruptedException e) { }
      }
   }
}
 
class DownloadTableModel extends AbstractTableModel implements Observer {
   // holds the strings to be displayed in the column headers of our table
   final String[] columnNames = {"Filename", "Filesize", "Progress"};
 
   // holds the data types for all our columns
   final Class[] columnClasses = {String.class, Integer.class, JProgressBar.class};
 
   // holds our data
   final Vector data = new Vector();
  
   // adds a row
   public void addDownload(Download d) {
      data.addElement(d);
   
      // the table model is interested in changes of the rows
      d.addObserver(this);
      fireTableRowsInserted(data.size()-1, data.size()-1);
   }
 
   // is called by a download object when its state changes
   public void update(Observable observable, Object o) {
      int index = data.indexOf(o);
      if (index != -1) 
         fireTableRowsUpdated(index, index);
   }
 
   public int getColumnCount() {
      return columnNames.length;
   }
         
   public int getRowCount() {
      return data.size();
   }
 
   public String getColumnName(int col) {
      return columnNames[col];
   }
 
   public Class getColumnClass(int c) {
      return columnClasses1;
   }
 
   public Object getValueAt(int row, int col) {
      Download download = (Download) data.elementAt(row);
      if (col == 0)      return download.getFilename();
      else if (col == 1) return new Integer(download.getFilesize());
      else if (col == 2) return new Float(download.getProgress());
      else return null;
   }
 
   public boolean isCellEditable(int row, int col) {
      return false;
   }
}
 
// a table cell renderer that displays a JProgressBar
class ProgressBarRenderer extends JProgressBar implements TableCellRenderer {
   public ProgressBarRenderer() {
      super();
   }
 
   public ProgressBarRenderer(BoundedRangeModel newModel) {
      super(newModel);
   }
 
   public ProgressBarRenderer(int orient) {
      super(orient);
   } 
 
   public ProgressBarRenderer(int min, int max) {
      super(min, max);
   }
 
   public ProgressBarRenderer(int orient, int min, int max) {
      super(orient, min, max);
   }
 
   public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
           
      setValue((int) ((Float) value).floatValue());
 
      return this;
   }
}