Swing/MultipleButtonsInTableCell のバックアップの現在との差分(No.21)
-
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:
hreflang:
href: https://java-swing-tips.blogspot.com/2009/10/multiple-jbuttons-in-jtable-cell.html lang: en
概要
概要
JTable
のセル内にクリック可能な複数のJButton
を配置します。
Screenshot
Advertisement
サンプルコード
サンプルコード
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) {
for (JButton b: buttons) {
b.setFocusable(false);
b.setRolloverEnabled(false);
add(b);
}
}
}
#spandel
View in GitHub: Java, Kotlin#spanend
#spanadd
#spanend
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) {
setBackground(isSelected?table.getSelectionBackground():table.getBackground());
#spanadd
#spanend
@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;
}
}
#spandel
#spanend
#spanadd
#spanend
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
// ---->
// 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()) {
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);
JOptionPane.showMessageDialog(table, "Editing: " + o);
}
});
addMouseListener(new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
fireEditingStopped();
}
});
}
#spanadd
#spanend
@Override public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
this.setBackground(table.getSelectionBackground());
return this;
}
#spanadd
#spanend
@Override public Object getCellEditorValue() {
return "";
}
//Copied from AbstractCellEditor
//protected EventListenerList listenerList = new EventListenerList();
#spanadd
#spanend
// Copied from AbstractCellEditor
// protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
@Override public boolean isCellEditable(java.util.EventObject e) {
return true;
}
#spandel
//......
#spanend
#spanadd
// ...
#spanend
解説
上記のサンプルでは、CellRenderer
用とCellEditor
用に、JButton
を2
つ配置したJPanel
をそれぞれ作成しています。アクションイベントを設定するのは、CellEditor
用のJButton
で、CellRenderer
用のJButton
は表示のためのダミーです。
解説
上記のサンプルでは、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
から必要なメソッドをコピーして回避する方法を使用している -
LookAndFeel
などが更新されたらJTable#updateUI()
メソッド内でSwingUtilities#updateRendererOrEditorUI()
メソッドを呼び出すなどして各セルレンダラーやセルエディタ(これらはJTable
の子コンポーネントではないので)を更新-
AbstractCellEditor
を継承するセルエディタはComponent
もDefaultCellEditor
も継承していないのでLookAndFeel
を変更しても追従しない - そのため
JTable#updateUI()
をオーバーライドしてセルエディタ自体を再作成するなどの対応が必要
-
- このサンプルでは、
Component
を継承(TableCellEditor
を実装)するセルエディタを作成し、AbstractCellEditor
から必要なメソッドをコピーして回避している
#spandel
//SwingUtilities#updateRendererOrEditorUI()
#spanend
#spanadd
// SwingUtilities#updateRendererOrEditorUI()
#spanend
static void updateRendererOrEditorUI(Object rendererOrEditor) {
if (rendererOrEditor == null) {
return;
}
Component component = null;
if (rendererOrEditor instanceof Component) {
component = (Component)rendererOrEditor;
component = (Component) rendererOrEditor;
}
if (rendererOrEditor instanceof DefaultCellEditor) {
component = ((DefaultCellEditor)rendererOrEditor).getComponent();
component = ((DefaultCellEditor) rendererOrEditor).getComponent();
}
if (component != null) {
SwingUtilities.updateComponentTreeUI(component);
}
}
JSpinner
(2
つのJButton
とJTextField
の組み合わせ)をCellEditor
に使用する
#spanend
#spandel
import java.awt.*;
#spanend
#spandel
import java.awt.event.*;
#spanend
#spandel
import java.util.*;
#spanend
#spandel
import java.util.List;
#spanend
#spandel
import javax.swing.*;
#spanend
#spandel
import javax.swing.event.*;
#spanend
#spandel
import javax.swing.table.*;
#spanend
#spandel
import javax.swing.text.*;
#spanend
#spandel
#spanend
#spandel
public class ButtonsInsideCellTest {
#spanend
private JComponent makeUI() {
String[] columnNames = {"Buttons", "Spinner"};
Object[][] data = {
{50, 100}, {100, 50}, {30, 20}, {0, 100}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
@Override public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
JTable table = new JTable(model);
table.setRowHeight(36);
table.setAutoCreateRowSorter(true);
#spandel
#spanend
TableColumn column = table.getColumnModel().getColumn(0);
column.setCellRenderer(new ButtonsRenderer());
column.setCellEditor(new ButtonsEditor());
#spandel
#spanend
column = table.getColumnModel().getColumn(1);
column.setCellRenderer(new SpinnerRenderer());
column.setCellEditor(new SpinnerEditor());
#spandel
#spanend
return new JScrollPane(table);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ButtonsInsideCellTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#spandel
}
#spanend
#spandel
#spanend
#spandel
class SpinnerPanel extends JPanel {
#spanend
public final JSpinner spinner = new JSpinner(new SpinnerNumberModel(100, 0, 200, 1));
public SpinnerPanel() {
super(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
#spandel
#spanend
c.weightx = 1.0;
c.insets = new Insets(0, 10, 0, 10);
c.fill = GridBagConstraints.HORIZONTAL;
#spandel
#spanend
setOpaque(true);
add(spinner, c);
}
#spandel
}
#spanend
#spandel
class SpinnerRenderer extends SpinnerPanel implements TableCellRenderer {
#spanend
public SpinnerRenderer() {
super();
setName("Table.cellRenderer");
}
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected?table.getSelectionBackground():table.getBackground());
spinner.setValue((Integer)value);
return this;
}
#spandel
}
#spanend
#spandel
#spanend
#spandel
class SpinnerEditor extends SpinnerPanel implements TableCellEditor {
#spanend
#spandel
// public SpinnerEditor(final JTable table, final int column) {
#spanend
#spandel
// super();
#spanend
#spandel
// }
#spanend
@Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
this.setBackground(table.getSelectionBackground());
spinner.setValue((Integer)value);
return this;
}
@Override public Object getCellEditorValue() {
//System.out.println("getCellEditorValue: " + spinner.getValue());
//try {
// spinner.commitEdit();
//} catch(Exception pe) {
// // Edited value is invalid, spinner.getValue() will return
// // the last valid value, you could revert the spinner to show that:
// JComponent editor = spinner.getEditor();
// if (editor instanceof JSpinner.DefaultEditor) {
// ((JSpinner.DefaultEditor)editor).getTextField().setValue(spinner.getValue());
// }
//}
return spinner.getValue();
}
#spandel
#spanend
//Copied from AbstractCellEditor
//protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
#spandel
#spanend
@Override public boolean isCellEditable(EventObject e) {
return true;
}
@Override public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override public boolean stopCellEditing() {
try {
spinner.commitEdit();
} catch(Exception pe) {
Toolkit.getDefaultToolkit().beep();
return false;
// 直前の値に戻して、編集を終了する場合
// Edited value is invalid, spinner.getValue() will return
// the last valid value, you could revert the spinner to show that:
//JComponent editor = spinner.getEditor();
//if (editor instanceof JSpinner.DefaultEditor) {
// ((JSpinner.DefaultEditor)editor).getTextField().setValue(spinner.getValue());
//}
}
fireEditingStopped();
return true;
}
@Override public void cancelCellEditing() {
fireEditingCanceled();
}
@Override public void addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class, l);
}
@Override public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listenerList.getListeners(CellEditorListener.class);
}
protected void fireEditingStopped() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
}
}
}
protected void fireEditingCanceled() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
}
}
}
#spandel
}
#spanend
#spandel
#spanend
#spandel
class ButtonsPanel extends JPanel {
#spanend
public final List<JButton> buttons = Arrays.asList(new JButton("+"), new JButton("-"));
public final JLabel label = new JLabel() {
@Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.width = 50;
return d;
}
};
public int i = -1;
public ButtonsPanel() {
super();
label.setHorizontalAlignment(SwingConstants.RIGHT);
setOpaque(true);
add(label);
for(JButton b: buttons) {
b.setFocusable(false);
b.setRolloverEnabled(false);
add(b);
}
}
#spandel
}
#spanend
#spandel
class ButtonsRenderer extends ButtonsPanel implements TableCellRenderer {
#spanend
public ButtonsRenderer() {
super();
setName("Table.cellRenderer");
}
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
this.setBackground(isSelected?table.getSelectionBackground():table.getBackground());
label.setText(value!=null?value.toString():"");
return this;
}
#spandel
}
#spanend
#spandel
class ButtonsEditor extends ButtonsPanel implements TableCellEditor {
#spanend
public ButtonsEditor() {
super();
buttons.get(0).addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
i++;
label.setText(""+i);
fireEditingStopped();
}
});
#spandel
#spanend
buttons.get(1).addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
i--;
label.setText(""+i);
fireEditingStopped();
}
});
#spandel
#spanend
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());
i = (Integer)value;
label.setText(""+i);
return this;
}
@Override public Object getCellEditorValue() {
return i;
}
#spandel
#spanend
//Copied from AbstractCellEditor
//protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
#spandel
#spanend
@Override public boolean isCellEditable(EventObject e) {
return true;
}
@Override public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
@Override public void cancelCellEditing() {
fireEditingCanceled();
}
@Override public void addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class, l);
}
@Override public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listenerList.getListeners(CellEditorListener.class);
}
protected void fireEditingStopped() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
}
}
}
protected void fireEditingCanceled() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
}
}
}
#spandel
}
#spanend
#spandel
参考リンク
参考リンク
- JTableのセルにJButtonを追加して行削除
- JTableのセルにHyperlinkを表示
- Table Button Column « Java Tips Weblog
- Table Button Column « Java Tips Weblog
- JTableのセル中にJRadioButtonを配置
- JTableのCellにJCheckBoxを複数配置する
- [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
で修正済
-