Adding a Component to a JTable

Question:
How do I add a JButton to the cells of a JTable?

Answer:
Using the Swing JTable class can become a sticky business when you want to customize it to your specific needs. First you must become familiar with how the JTable class is organized. Individual cells are rendered by TableCellRenderer implementations. The table contents are represented by animplementation of the TableModel interface. By default, JTable uses DefaultTableCellRenderer to draw its cells. DefaultTableCellRendererrecognizes a few primitive types, rendering them as strings, and can even display Boolean types as checkboxes. But it defaults to displaying the value returned by toString() for types it does notspecifically handle.

You have to provide your own TableCellRenderer implementation if you want to display buttons in a JTable. The TableCellRenderer interfacecontains only one method, getTableCellRendererComponent(...), which returns a java.awt.Component that knows how to draw the contents of a specific cell. Usually, getTableCellRendererComponent() will return the same component for every cell of a column to avoid the unnecessary use of extra memory. But when the contents of a cell isitself a component, it is all right to return that component as the renderer. Therefore, the first step towards having JButtons display correctly in a JTable is to create a TableCellRenderer implementation that returns the JButton contained in the cell being rendered. In the accompanying code listing, JTableButtonRenderer demonstrates how to do this.

Even after creating a custom TableCellRenderer, you’re still not done. The TableModel associated with a given JTable does not only keep track of the contents of each cell, but it also keeps track of the classof data stored in each column. DefaultTableModel is designed towork with DefaultTableCellRenderer and will return java.lang.String.class for columns containing data types that it does not specifically handle. The exact method that does this is getColumnClass(int column).

Your second step is to create a TableModel implementation that returns JButton.class for cells that contain JButtons. JTableButtonModel shows one way to do this. It just returns the result of getClass() for each piece of cell data.

At this point, you’re almost done, but not quite. What’s the use of putting a JButton in a JTable if you can’t press the darn thing?

By default, JTable will not forward mouse events to components contained in its cells. If you want to be able to press the buttons you add to JTable, you have to create your own MouseListener that forwards events to the JButton cells. JTableButtonMouseListener demonstrates how you could do this.

Yep, you’re done now. The code listing shows how tie all these pieces together.

import com.sun.java.swing.*;import com.sun.java.swing.table.*;import java.awt.*;import java.awt.event.*;class JTableButtonRenderer implements TableCellRenderer {  private TableCellRenderer __defaultRenderer;  public JTableButtonRenderer(TableCellRenderer renderer) {    __defaultRenderer = renderer;  }  public Component getTableCellRendererComponent(JTable table, Object value,						 boolean isSelected,						 boolean hasFocus,						 int row, int column)  {    if(value instanceof Component)      return (Component)value;    return __defaultRenderer.getTableCellRendererComponent(	   table, value, isSelected, hasFocus, row, column);  }}class JTableButtonModel extends AbstractTableModel {  private Object[][] __rows = {      { "One", new JButton("Button One") },      { "Two", new JButton("Button Two") },      { "Three", new JButton("Button Three") },      { "Four", new JButton("Button Four") }    };  private String[] __columns = { "Numbers", "Buttons" };  public String getColumnName(int column) {     return __columns[column];  }  public int getRowCount() {    return __rows.length;  }  public int getColumnCount() {    return __columns.length;  }  public Object getValueAt(int row, int column) {       return __rows[row][column];  }  public boolean isCellEditable(int row, int column) {    return false;  }  public Class getColumnClass(int column) {    return getValueAt(0, column).getClass();  }}class JTableButtonMouseListener implements MouseListener {  private JTable __table;  private void __forwardEventToButton(MouseEvent e) {    TableColumnModel columnModel = __table.getColumnModel();    int column = columnModel.getColumnIndexAtX(e.getX());    int row    = e.getY() / __table.getRowHeight();    Object value;    JButton button;    MouseEvent buttonEvent;    if(row >= __table.getRowCount() || row < 0 ||       column >= __table.getColumnCount() || column < 0)      return;    value = __table.getValueAt(row, column);    if(!(value instanceof JButton))      return;    button = (JButton)value;    buttonEvent =      (MouseEvent)SwingUtilities.convertMouseEvent(__table, e, button);    button.dispatchEvent(buttonEvent);    // This is necessary so that when a button is pressed and released    // it gets rendered properly.  Otherwise, the button may still appear    // pressed down when it has been released.    __table.repaint();  }  public JTableButtonMouseListener(JTable table) {    __table = table;  }  public void mouseClicked(MouseEvent e) {    __forwardEventToButton(e);  }  public void mouseEntered(MouseEvent e) {    __forwardEventToButton(e);  }  public void mouseExited(MouseEvent e) {    __forwardEventToButton(e);  }  public void mousePressed(MouseEvent e) {    __forwardEventToButton(e);  }  public void mouseReleased(MouseEvent e) {    __forwardEventToButton(e);  }}public final class JTableButton extends JFrame {  private JTable __table;  private JScrollPane __scrollPane;  public JTableButton() {    super("JTableButton Demo");    TableCellRenderer defaultRenderer;    __table = new JTable(new JTableButtonModel());    defaultRenderer = __table.getDefaultRenderer(JButton.class);    __table.setDefaultRenderer(JButton.class,			       new JTableButtonRenderer(defaultRenderer));    __table.setPreferredScrollableViewportSize(new Dimension(400, 200));    __table.addMouseListener(new JTableButtonMouseListener(__table));    __scrollPane = new JScrollPane(__table);    setContentPane(__scrollPane);  }  public static void main(String[] args) {    Frame frame;    WindowListener exitListener;    exitListener = new WindowAdapter() {      public void windowClosing(WindowEvent e) {	Window window = e.getWindow();	window.setVisible(false);	window.dispose();	System.exit(0);      }    };    frame = new JTableButton();    frame.addWindowListener(exitListener);    frame.pack();    frame.setVisible(true);  }}
Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

microsoft careers

Top Careers at Microsoft

Microsoft has gained its position as one of the top companies in the world, and Microsoft careers are flourishing. This multinational company is efficiently developing popular software and computers with other consumer electronics. It is a dream come true for so many people to acquire a high paid, high-prestige job

your company's audio

4 Areas of Your Company Where Your Audio Really Matters

Your company probably relies on audio more than you realize. Whether you’re creating a spoken text message to a colleague or giving a speech, you want your audio to shine. Otherwise, you could cause avoidable friction points and potentially hurt your brand reputation. For example, let’s say you create a

chrome os developer mode

How to Turn on Chrome OS Developer Mode

Google’s Chrome OS is a popular operating system that is widely used on Chromebooks and other devices. While it is designed to be simple and user-friendly, there are times when users may want to access additional features and functionality. One way to do this is by turning on Chrome OS