---
category: swing
folder: MultipleButtonsInTableCell
title: JTableのセルに複数のJButtonを配置する
tags: [JTable, TableCellEditor, TableCellRenderer, JButton, JPanel, ActionListener]
author: aterai
pubdate: 2009-10-05T12:57:02+09:00
description: JTableのセル内にクリック可能な複数のJButtonを配置します。
image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTQRygoYeI/AAAAAAAAAfU/-Sr9o7PsQkM/s800/MultipleButtonsInTableCell.png
hreflang:
href: https://java-swing-tips.blogspot.com/2009/10/multiple-jbuttons-in-jtable-cell.html
lang: en
---
* Summary [#summary]
`JTable`のセル内にクリック可能な複数の`JButton`を配置します。
#download(https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTQRygoYeI/AAAAAAAAAfU/-Sr9o7PsQkM/s800/MultipleButtonsInTableCell.png)
* Source Code Examples [#sourcecode]
#code(link){{
class ButtonsPanel extends JPanel {
public final List<JButton> buttons =
Arrays.asList(new JButton("view"), new JButton("edit"));
public ButtonsPanel() {
super();
setOpaque(true);
for (JButton b: buttons) {
b.setFocusable(false);
b.setRolloverEnabled(false);
add(b);
}
}
}
class ButtonsRenderer extends ButtonsPanel
implements TableCellRenderer {
public ButtonsRenderer() {
super();
setName("Table.cellRenderer");
}
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Color bgc = isSelected ? table.getSelectionBackground()
: table.getBackground()
setBackground(bgc);
return this;
}
}
class ButtonsEditor extends ButtonsPanel
implements TableCellEditor {
public ButtonsEditor(final JTable table) {
super();
// ---->
// DEBUG: view button click
// -> control key down + edit button(same cell) press
// -> remain selection color
MouseListener ml = new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
ButtonModel m = ((JButton) e.getSource()).getModel();
if (m.isPressed() && table.isRowSelected(table.getEditingRow())
&& e.isControlDown()) {
setBackground(table.getBackground());
}
}
};
buttons.get(0).addMouseListener(ml);
buttons.get(1).addMouseListener(ml);
// <----
buttons.get(0).addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
fireEditingStopped();
JOptionPane.showMessageDialog(table, "Viewing");
}
});
buttons.get(1).addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
int row = table.convertRowIndexToModel(table.getEditingRow());
Object o = table.getModel().getValueAt(row, 0);
fireEditingStopped();
JOptionPane.showMessageDialog(table, "Editing: " + o);
}
});
addMouseListener(new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
fireEditingStopped();
}
});
}
@Override public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
this.setBackground(table.getSelectionBackground());
return this;
}
@Override public Object getCellEditorValue() {
return "";
}
// Copied from AbstractCellEditor
// protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
@Override public boolean isCellEditable(java.util.EventObject e) {
return true;
}
// ...
}}
* Description [#explanation]
* Description [#description]
上記のサンプルでは、`CellRenderer`用と`CellEditor`用に`JButton`を`2`つ配置した`JPanel`をそれぞれ作成しています。`CellRenderer`用の`JButton`は表示のみに使用するため、クリック後に実行する`Action`は`CellEditor`用の`JButton`だけに設定しています。
----
- `LookAndFeel`などが更新されたら`JTable#updateUI()`メソッド内で`SwingUtilities#updateRendererOrEditorUI()`メソッドを呼び出すなどして各セルレンダラーやセルエディタ(これらは`JTable`の子コンポーネントではないので)を更新
-- `AbstractCellEditor`を継承するセルエディタは`Component`も`DefaultCellEditor`も継承していないので`LookAndFeel`を変更しても追従しない
-- そのため`JTable#updateUI()`をオーバーライドしてセルエディタ自体を再作成するなどの対応が必要
- このサンプルでは、`Component`を継承(`TableCellEditor`を実装)するセルエディタを作成し、`AbstractCellEditor`から必要なメソッドをコピーして回避している
#code{{
// SwingUtilities#updateRendererOrEditorUI()
static void updateRendererOrEditorUI(Object rendererOrEditor) {
if (rendererOrEditor == null) {
return;
}
Component component = null;
if (rendererOrEditor instanceof Component) {
component = (Component) rendererOrEditor;
}
if (rendererOrEditor instanceof DefaultCellEditor) {
component = ((DefaultCellEditor) rendererOrEditor).getComponent();
}
if (component != null) {
SwingUtilities.updateComponentTreeUI(component);
}
}
}}
----
- `JSpinner`(`2`つの`JButton`と`JTextField`の組み合わせ)を`CellEditor`に使用する
-- [[JTableのCellEditorにJPanelを使用して複数コンポーネントを配置>Swing/PanelCellEditorRenderer]]に移動
* Reference [#reference]
- [[JTableのセルにJButtonを追加して行削除>Swing/DeleteButtonInCell]]
- [[JTableのセルにHyperlinkを表示>Swing/HyperlinkInTableCell]]
- [https://tips4java.wordpress.com/2009/07/12/table-button-column/ Table Button Column « Java Tips Weblog]
- [[JTableのセル中にJRadioButtonを配置>Swing/RadioButtonsInTableCell]]
- [[JTableのCellにJCheckBoxを複数配置する>Swing/CheckBoxesInTableCell]]
- [https://bugs.openjdk.org/browse/JDK-8138667 [JDK-8138667] java.lang.IllegalAccessError: tried to access method (for a protected method) - Java Bug System]
-- `Java 8`で`EventQueue.invokeLater( () -> fireEditingStopped() );`ではなく`EventQueue.invokeLater(ButtonsEditor.this::fireEditingStopped);`のようなコードを実行すると`IllegalAccessError`になる場合がある
-- `Java 9`で修正済
* Comment [#comment]
#comment
- 第`0`列目が編集状態でボタンをクリックした場合、パネルが`2`度表示されるバグを修正。 -- &user(aterai); &new{2009-10-06 (火) 11:56:21};
- [https://tips4java.wordpress.com/2009/07/12/table-button-column/ Table Button Column « Java Tips Weblog]を参考にして、`JTable#editCellAt`ではなく、逆に`TableCellEditor#stopCellEditing()`を使用してクリック直後に編集終了するように変更。 -- &user(aterai); &new{2009-11-03 (火) 04:36:55};
- KBD{Ctrl}キーを押しながら、`edit`ボタンをクリックすると異なる行(`table.getSelectedRow()`)の内容が表示されるバグを修正。 -- &user(aterai); &new{2011-03-10 (木) 02:35:35};
- すごいと思いました! -- &user(いわく); &new{2013-05-24 (金) 11:57:26};
-- こんばんは。たしかに`JTable`の`TableCellRenderer`、`TableCellEditor`の仕組みは、すごい良くできているといつも感心してしまいます :) -- &user(aterai); &new{2013-05-24 (金) 23:59:16};
#comment