TITLE:JTableのセルにJButtonを追加して行削除

Posted by terai at 2007-10-22

JTableのセルにJButtonを追加して行削除

JTableのセルにJButtonを追加し、クリックされたらその行を削除します。主にSwing - JButton inside JTable Cellの投稿を参考にしています。

  • &jnlp;
  • &jar;
  • &zip;

#screenshot

サンプルコード

class ButtonColumn extends AbstractCellEditor
    implements TableCellRenderer, TableCellEditor {
  private static final String LABEL = "X";
  private final JButton renderButton = new JButton(LABEL);
  private final JButton editorButton;
  public ButtonColumn() {
    super();
    editorButton = new JButton(new AbstractAction(LABEL) {
      public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
      }
    });
    editorButton.setBorder(BorderFactory.createEmptyBorder());
    renderButton.setBorder(BorderFactory.createEmptyBorder());
    editorButton.setFocusPainted(false);
    editorButton.setRolloverEnabled(false);
    //renderButton.setToolTipText("Delete(renderButton)");
    //editorButton.setToolTipText("Delete(editorButton)");
  }
  public Component getTableCellRendererComponent(JTable table, Object value,
                      boolean isSelected, boolean hasFocus, int row, int column) {
    return renderButton;
  }
  public Component getTableCellEditorComponent(JTable table, Object value,
                      boolean isSelected, int row, int column) {
    return editorButton;
  }
  public Object getCellEditorValue() {
    return LABEL;
  }
}

解説

上記のサンプルでは、ボタンがクリックされたときの削除自体は、JTableに追加したマウスリスナーで行っており、セルエディタやセルレンダラーに使っているJButtonは表示のためのダミーです。

table.addMouseListener(new MouseAdapter() {
  private int targetRow = -1;
  @Override public void mousePressed(MouseEvent e) {
    Point pt = e.getPoint();
    int mcol = table.convertColumnIndexToModel(table.columnAtPoint(pt));
    int vrow = table.rowAtPoint(e.getPoint());
    int mrow = (vrow>=0)?table.convertRowIndexToModel(vrow):-1;
    if(mrow>=0 && mcol==BUTTON_COLUMN) {
      targetRow = mrow;
    }
  }
  @Override public void mouseReleased(MouseEvent e) {
    Point pt = e.getPoint();
    int mcol = table.convertColumnIndexToModel(table.columnAtPoint(pt));
    int vrow = table.rowAtPoint(e.getPoint());
    int mrow = (vrow>=0)?table.convertRowIndexToModel(vrow):-1;
    if(targetRow==mrow && mcol==BUTTON_COLUMN) {
      model.removeRow(mrow);
    }
    targetRow = -1;
  }
});

複数選択を許可しない場合は、Swing - JButton inside JTable Cellのように、セルエディタにリスナーを追加し、選択されたセルを削除する方法もあります。

public ButtonColumn(final JTable table) {
  super();
  table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  editorButton = new JButton(new AbstractAction(LABEL) {
    public void actionPerformed(ActionEvent e) {
      fireEditingStopped();
      int row = table.convertRowIndexToModel(table.getSelectedRow());
      ((DefaultTableModel)table.getModel()).removeRow(row);
    }
  });
  //...

複数選択を許可する場合は、Ctrlキーなどを押しながらのボタンクリックに注意する必要があります。

参考リンク

コメント

  • ボタンのセル内でマウスを移動しても削除するように変更。 -- terai
    • メモ: 0行目のボタンをクリックし、真上のヘッダ上でリリースしても削除できる -> Bug ID: 6291631 JTable: rowAtPoint returns 0 for negative y -- terai
      //上記のBug Databaseにある回避方法
      JTable table = new JTable(model) {
        @Override public int rowAtPoint(Point pt) {
          return (pt.y<0)?-1:super.rowAtPoint(pt);
        }
      };
      
  • テスト -- terai
    import java.awt.*;
    import java.awt.event.*;
    import java.util.EventObject;
    import javax.swing.*;
    import javax.swing.table.*;
    public class MultipleButtonsInCellTest2 {
      JTable t;
      public JComponent makeUI() {
        String[] columnNames = {"String", "Button"};
        Object[][] data = { {"AAA", null}, {"BBB", null} };
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {
          @Override public Class<?> getColumnClass(int column) {
            return getValueAt(0, column).getClass();
          }
        };
        t = new JTable(model);
        t.addMouseListener(new MouseAdapter() {
          private boolean isOnButtonColumn(MouseEvent e) {
            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(Point p) {
            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)) {
              Point pt = e.getPoint();
              component = getCellComponentAt(pt);
              t.editCellAt(t.rowAtPoint(pt), t.columnAtPoint(pt));
            }
          }
          @Override public void mouseReleased(MouseEvent e) {
            if(isOnButtonColumn(e)) {
              Component c = getCellComponentAt(e.getPoint());
              if(c instanceof JButton && c==component) ((JButton)c).doClick();
              component = null;
            }
          }
          Component component = null;
        });
        t.setRowHeight(36);
        ActionPanelEditorRenderer er = new ActionPanelEditorRenderer();
        TableColumn column = t.getColumnModel().getColumn(1);
        column.setCellRenderer(er);
        column.setCellEditor(er);
        JPanel p = new JPanel(new BorderLayout());
        p.add(new JScrollPane(t));
        p.setPreferredSize(new Dimension(320, 200));
        return p;
      }
      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 MultipleButtonsInCellTest2().makeUI());
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
      }
    }
    class ActionPanelEditorRenderer extends AbstractCellEditor
                                    implements TableCellRenderer,TableCellEditor{
      JPanel renderer = new JPanel();
      JPanel editor   = new JPanel();
      public ActionPanelEditorRenderer() {
        super();
        JButton viewButton2 = new JButton(new AbstractAction("view2") {;
          public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null, "Viewing");
          }
        });
        JButton editButton2 = new JButton(new AbstractAction("edit2") {;
          public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null, "Editing");
          }
        });
        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 null; }
    }