sing the Swing JTable class can quickly 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 an
implementation of the TableModel interface. By default, JTable uses
DefaultTableCellRendererto 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 not specifically handle.
You have to provide your own TableCellRenderer implementation if
you want to display buttons in a JTable. The TableCellRenderer
interface contains 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 is
itself 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 class of data stored in each column.
DefaultTableModel is designed to work with DefaultTableCellRendererand 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 to tie all
these pieces together.
-------------------------------------------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
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);
}
}
--------------------------------------------
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
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);
}
}
-------------------------------------------------------------