TITLE:JTableのセルに複数のJButtonを配置する
Posted by terai at 2009-10-05

JTableのセルに複数のJButtonを配置する

JTableのセル内にクリック可能な複数のJButtonを配置します。
  • 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

概要

JTableのセル内にクリック可能な複数のJButtonを配置します。

#screenshot

サンプルコード

#spanend
#spanadd
class ButtonsPanel extends JPanel {
#spanend
  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);
    }
  }
#spanadd
}
#spanend

#spandel
**サンプルコード [#ybe99159]
#spanend
#spandel
#code{{
#spanend
#spandel
class ButtonsEditorRenderer extends AbstractCellEditor
#spanend
                            implements TableCellRenderer,TableCellEditor{
  private final JPanel renderer = new JPanel();
  private final JPanel editor   = new JPanel();
  public ButtonsEditorRenderer(final JTable table) {
#spanadd
class ButtonsRenderer extends ButtonsPanel
#spanend
                      implements TableCellRenderer {
  public ButtonsRenderer() {
    super();
    JButton viewButton2 = new JButton(new AbstractAction("view2") {;
      @Override
      public void actionPerformed(ActionEvent e) {
    setName("Table.cellRenderer");
  }
#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;
  }
#spanadd
}
#spanend
#spanadd

#spanend
#spanadd
class ButtonsEditor extends ButtonsPanel
#spanend
                    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);
    // <----
#spanadd

#spanend
    buttons.get(0).addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
        JOptionPane.showMessageDialog(table, "Viewing");
      }
    });
    JButton editButton2 = new JButton(new AbstractAction("edit2") {;
      @Override
      public void actionPerformed(ActionEvent e) {
        //int row = table.convertRowIndexToModel(table.getSelectedRow());
        int row = table.getSelectedRow();
#spanadd

#spanend
    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);
        JOptionPane.showMessageDialog(table, "Editing: "+o);
        fireEditingStopped();
        JOptionPane.showMessageDialog(table, "Editing: " + o);
      }
    });
    renderer.setOpaque(true);
    renderer.add(new JButton("view1"));
    renderer.add(new JButton("edit1"));
    editor.setOpaque(true);
    editor.add(viewButton2);
    editor.add(editButton2);
#spanadd

#spanend
    addMouseListener(new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        fireEditingStopped();
      }
    });
  }
  @Override
  public Component getTableCellRendererComponent(
    JTable table, Object value, boolean isSelected, boolean hasFocus,
    int row, int column) {
    renderer.setBackground(isSelected?table.getSelectionBackground()
                     :table.getBackground());
    return renderer;
#spanadd

#spanend
  @Override public Component getTableCellEditorComponent(JTable table,
        Object value, boolean isSelected, int row, int column) {
    this.setBackground(table.getSelectionBackground());
    return this;
  }
  @Override
  public Component getTableCellEditorComponent(
    JTable table, Object value, boolean isSelected, int row, int column) {
    editor.setBackground(table.getSelectionBackground());
    return editor;
  }
  @Override
  public Object getCellEditorValue() {
#spanadd

#spanend
  @Override public Object getCellEditorValue() {
    return "";
  }
#spandel
}
#spanend
#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;
  }
#spanadd
// ...
#spanend
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、CellRenderer用とCellEditor用に、JButtonを2つ配置したJPanelをそれぞれ作成しています。アクションイベントを設定するのは、CellEditor用のJButtonで、CellRenderer用のJButtonは表示のためのダミーです。

解説

上記のサンプルでは、CellRenderer用とCellEditor用にJButton2つ配置したJPanelをそれぞれ作成しています。CellRenderer用のJButtonは表示のみに使用するため、クリック後に実行するActionCellEditor用のJButtonだけに設定しています。
行の選択状態が切り替わるとき(例えば、一行目を選択していて、二行目のボタンをクリック)には、CellEditor用のJButtonがうまくクリックできないので、JTable自体に以下のようなマウスリスナーを設定しています。
  • LookAndFeelなどが更新されたらJTable#updateUI()メソッド内でSwingUtilities#updateRendererOrEditorUI()メソッドを呼び出すなどして各セルレンダラーやセルエディタ(これらはJTableの子コンポーネントではないので)を更新
    • AbstractCellEditorを継承するセルエディタはComponentDefaultCellEditorも継承していないのでLookAndFeelを変更しても追従しない
    • そのためJTable#updateUI()をオーバーライドしてセルエディタ自体を再作成するなどの対応が必要
  • このサンプルでは、Componentを継承(TableCellEditorを実装)するセルエディタを作成し、AbstractCellEditorから必要なメソッドをコピーして回避している
#spandel
class CellButtonsMouseListener extends MouseAdapter{
#spanend
  private Component component = null;
  private boolean isOnButtonColumn(MouseEvent e) {
    JTable t = (JTable)e.getComponent();
    Point pt = e.getPoint();
    int mcol = t.convertColumnIndexToModel(t.columnAtPoint(pt));
    int vrow = t.rowAtPoint(pt);
    int mrow = (vrow>=0)?t.convertRowIndexToModel(vrow):-1;
    return mrow>=0 && mcol==1;
#spanadd
// SwingUtilities#updateRendererOrEditorUI()
#spanend
#spanadd
static void updateRendererOrEditorUI(Object rendererOrEditor) {
#spanend
  if (rendererOrEditor == null) {
    return;
  }
  private Component getCellComponentAt(MouseEvent e) {
    JTable t = (JTable)e.getComponent();
    Point p  = e.getPoint();
    int row  = t.rowAtPoint(p);
    int col  = t.columnAtPoint(p);
    TableCellEditor ce = t.getCellEditor(row,col);
    Component c = ce.getTableCellEditorComponent(t,null,true,row,col);
    Point pt = SwingUtilities.convertPoint(t, p, c);
    return SwingUtilities.getDeepestComponentAt(c, pt.x, pt.y);
  Component component = null;
  if (rendererOrEditor instanceof Component) {
    component = (Component) rendererOrEditor;
  }
  @Override
  public void mousePressed(MouseEvent e) {
    if(isOnButtonColumn(e)) {
      EventQueue.invokeLater(new Runnable() {
        public void run() {
          JTable t = (JTable)e.getComponent();
          Point pt = e.getPoint();
          component = getCellComponentAt(e);
          component.requestFocusInWindow();
          t.editCellAt(t.rowAtPoint(pt), t.columnAtPoint(pt));
        }
      });
    }
  if (rendererOrEditor instanceof DefaultCellEditor) {
    component = ((DefaultCellEditor) rendererOrEditor).getComponent();
  }
  @Override
  public void mouseReleased(MouseEvent e) {
    if(isOnButtonColumn(e)) {
      Component c = getCellComponentAt(e);
      if(c instanceof JButton && c==component) ((JButton)c).doClick();
      component = null;
    }
  if (component != null) {
    SwingUtilities.updateComponentTreeUI(component);
  }
}
  • -

参考リンク

参考リンク

コメント

  • 第0列目が編集状態でボタンをクリックした場合、パネルが二度表示されるバグを修正。 -- terai

コメント