TITLE:JTableのセルに複数のJButtonを配置する
#navi(../)
RIGHT:Posted by [[terai]] at 2009-10-05
*JTableのセルに複数のJButtonを配置する [#wd1c2d32]
JTableのセル内にクリック可能な複数のJButtonを配置します。

-&jnlp;
-&jar;
-&zip;

#screenshot

**サンプルコード [#ybe99159]
#code{{
class ButtonsEditorRenderer extends AbstractCellEditor
                            implements TableCellRenderer,TableCellEditor{
  private final JPanel renderer = new JPanel();
  private final JPanel editor   = new JPanel();
  public ButtonsEditorRenderer(final JTable table) {
    super();
    JButton viewButton2 = new JButton(new AbstractAction("view2") {;
      @Override
      public void actionPerformed(ActionEvent e) {
        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();
        Object o = table.getModel().getValueAt(row, 0);
        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);
  }
  @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;
  }
  @Override
  public Component getTableCellEditorComponent(
    JTable table, Object value, boolean isSelected, int row, int column) {
    editor.setBackground(table.getSelectionBackground());
    return editor;
  }
  @Override
  public Object getCellEditorValue() {
    return "";
  }
}
}}

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

----
行の選択状態が切り替わるとき(例えば、一行目を選択していて、二行目のボタンをクリック)には、CellEditor用のJButtonがうまくクリックできないので、JTable自体に以下のようなマウスリスナーを設定しています。

#code{{
class CellButtonsMouseListener extends MouseAdapter{
  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;
  }
  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);
  }
  @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));
        }
      });
    }
  }
  @Override
  public void mouseReleased(MouseEvent e) {
    if(isOnButtonColumn(e)) {
      Component c = getCellComponentAt(e);
      if(c instanceof JButton && c==component) ((JButton)c).doClick();
      component = null;
    }
  }
}
}}


**参考リンク [#b4c9d73f]
-[[JTableのセルにJButtonを追加して行削除>Swing/DeleteButtonInCell]]
-[[JTableのセルにHyperlinkを表示>Swing/HyperlinkInTableCell]]

**コメント [#le0df76f]
- 第0列目が編集状態でボタンをクリックした場合、パネルが二度表示されるバグを修正。 -- [[terai]] &new{2009-10-06 (火) 11:56:21};

#comment