TITLE:JTableのCellにJCheckBoxを複数配置する
Posted by aterai at 2011-02-28

JTableのCellにJCheckBoxを複数配置する

JTableのセル中にJCheckBoxを複数個配置します。
  • category: swing folder: CheckBoxesInTableCell title: JTableのCellにJCheckBoxを複数配置する tags: [JTable, JCheckBox, TableCellRenderer, TableCellEditor, JPanel, InputMap, ActionMap] author: aterai pubdate: 2011-02-28T15:07:56+09:00 description: JTableのセル中にJCheckBoxを複数個配置します。 image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TWs6JY73P8I/AAAAAAAAA2M/wwrwT7R5K4k/s800/CheckBoxesInTableCell.png hreflang:
       href: https://java-swing-tips.blogspot.com/2011/03/checkboxes-in-jtable-cell.html
       lang: en

概要

JTableのセル中にJCheckBoxを複数個配置します。
CheckBoxesInTableCell.png

サンプルコード

#spanend
#spandel
class CheckBoxEditorRenderer extends AbstractCellEditor
#spanend
      implements TableCellRenderer, TableCellEditor {
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
#spanadd
class CheckBoxesPanel extends JPanel {
#spanend
  private static final String OSNAME = System.getProperty("os.name");
  protected final String[] title = {"r", "w", "x"};
  protected final CheckBoxPanel editor = new CheckBoxPanel(title);
  protected final CheckBoxPanel renderer = new CheckBoxPanel(title);
  protected void updateButtons(CheckBoxPanel p, Object v) {
    Integer i = (Integer)(v==null?0:v);
    p.buttons[0].setSelected((i&(1<<2))!=0);
    p.buttons[1].setSelected((i&(1<<1))!=0);
    p.buttons[2].setSelected((i&(1<<0))!=0);
  public JCheckBox[] buttons;
  public CheckBoxesPanel() {
    super();
    setOpaque(false);
    setBackground(new Color(0x0, true));
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    initButtons();
  }
  @Override public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row, int col) {
    updateButtons(renderer, value);
    return renderer;
#spanadd

#spanend
  protected void initButtons() {
    buttons = new JCheckBox[title.length];
    for (int i = 0; i < buttons.length; i++) {
      JCheckBox b = new JCheckBox(title[i]);
      b.setOpaque(false);
      b.setFocusable(false);
      b.setRolloverEnabled(false);
      b.setBackground(new Color(0x0, true));
      buttons[i] = b;
      add(b);
      add(Box.createHorizontalStrut(5));
    }
  }
  @Override public Component getTableCellEditorComponent(JTable table,
      Object value, boolean isSelected, int row, int column) {
    updateButtons(editor, value);
    return editor;
  }
  @Override public Object getCellEditorValue() {
    int i = 0;
    if(editor.buttons[0].isSelected()) i|=1<<2;
    if(editor.buttons[1].isSelected()) i|=1<<1;
    if(editor.buttons[2].isSelected()) i|=1<<0;
    return i;
  }
#spandel
}
#spanend
#spandel

解説

上記のサンプルでは、JTableのCell内に3つのJCheckBoxを配置したJPanelを作成し、これをCellRendererとCellEditorとして別々に使用しています。JCheckBoxをマウスでクリックすると、そのJCheckBoxの選択状態だけが変化します(注: キーボードからの入力には対応していません)。
  • - ヘッダカラムの移動、リサイズ(JFrameなどのリサイズ)で、チェックした内容が消えてしまわないように、JTable自体に以下の様なMouseListenerを追加してチェックボックスがクリックされるたびにtable.getCellEditor(row, col).stopCellEditing();を呼び出しています。
#spanend
#spandel
table.addMouseListener(new MouseAdapter() {
#spanend
  @Override public void mouseReleased(MouseEvent e) {
    JTable t = (JTable)e.getComponent();
    Point p  = e.getPoint();
    int row  = t.rowAtPoint(p);
    int col  = t.columnAtPoint(p);
    if(t.convertColumnIndexToModel(col)==1) {
      t.getCellEditor(row, col).stopCellEditing();
  protected void updateButtons(Object v) {
    if ("Windows 7".equals(OSNAME)) { //Windows aero?
      removeAll();
      initButtons();
    }
    Integer i = (Integer) (v == null ? 0 : v);
    buttons[0].setSelected((i & (1 << 2)) != 0);
    buttons[1].setSelected((i & (1 << 1)) != 0);
    buttons[2].setSelected((i & (1 << 0)) != 0);
  }
#spandel
});
#spanend
#spandel
} JTableにではなく、CellEditor自体にMouseListenerを追加する方法もあります。
#spanend
#spandel
class CheckBoxEditorRenderer2 extends CheckBoxEditorRenderer
#spanend
                              implements MouseListener {
  private final JTable table;
  public CheckBoxEditorRenderer2(JTable table) {
#spanadd
class CheckBoxesRenderer extends CheckBoxesPanel
#spanend
                         implements TableCellRenderer, Serializable {
  public CheckBoxesRenderer() {
    super();
    this.table = table;
    editor.addMouseListener(this);
    setName("Table.cellRenderer");
  }
  //Copied form http://tips4java.wordpress.com/2009/07/12/table-button-column/
  private boolean isButtonColumnEditor;
  @Override public void mousePressed(MouseEvent e) {
    if(table.isEditing() &&  table.getCellEditor() == this) {
      isButtonColumnEditor = true;
    }
#spanadd

#spanend
  @Override public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    updateButtons(value);
    return this;
  }
  @Override public void mouseReleased(MouseEvent e) {
    if(isButtonColumnEditor &&  table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
    isButtonColumnEditor = false;
  }
  @Override public void mouseClicked(MouseEvent e) {}
  @Override public void mouseEntered(MouseEvent e) {}
  @Override public void mouseExited(MouseEvent e) {}
#spanadd

#spanend
  public static class UIResource extends CheckBoxesRenderer implements UIResource {}
}
#spandel

参考リンク

コメント

  • EnumSetのテスト -- aterai
    #spanend
    #spandel
    import java.awt.*;
    #spanend
    #spandel
    import java.awt.event.*;
    #spanend
    #spandel
    import java.util.*;
    #spanend
    #spandel
    import javax.swing.*;
    #spanend
    #spandel
    import javax.swing.table.*;
    #spanend
    #spandel
    public class EnumSetTest {
    #spanend
      public JComponent makeUI() {
        String[] columnNames = { "rwx", "user" };
        Object[][] data = {
          {"read",    EnumSet.of(User.OWNER)},
          {"write",   EnumSet.of(User.OWNER, User.GROUP)},
          {"execute", EnumSet.of(User.OWNER, User.GROUP, User.OTHER)}
        };
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {
          @Override public Class<?> getColumnClass(int column) {
            return getValueAt(0, column).getClass();
    #spanadd
    class CheckBoxesEditor extends CheckBoxesPanel
    #spanend
                           implements TableCellEditor, Serializable {
      public CheckBoxesEditor() {
        ActionListener al = new ActionListener() {
          @Override public void actionPerformed(ActionEvent e) {
            fireEditingStopped();
          }
        };
        JTable table = new JTable(model);
        table.addMouseListener(new MouseAdapter() {
          @Override public void mouseReleased(MouseEvent e) {
            JTable t = (JTable)e.getComponent();
            Point p  = e.getPoint();
            int row  = t.rowAtPoint(p);
            int col  = t.columnAtPoint(p);
            if (t.convertColumnIndexToModel(col)==1) {
              t.getCellEditor(row, col).stopCellEditing();
        ActionMap am = getActionMap();
        for (int i = 0; i < buttons.length; i++) {
          final JCheckBox b = buttons[i];
          b.addActionListener(al);
          am.put(title[i], new AbstractAction(title[i]) {
            @Override public void actionPerformed(ActionEvent e) {
              b.setSelected(!b.isSelected());
              fireEditingStopped();
            }
          }
        });
        CheckBoxEditorRenderer cer = new CheckBoxEditorRenderer();
        TableColumn c = table.getColumnModel().getColumn(1);
        c.setCellRenderer(cer);
        c.setCellEditor(cer);
        c.setPreferredWidth(180);
    #spandel
    
    #spanend
        JPanel p = new JPanel(new BorderLayout());
        p.add(new JScrollPane(table));
        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 EnumSetTest().makeUI());
        f.setSize(320,200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
      }
    #spandel
    }
    #spanend
    #spandel
    class CheckBoxPanel extends JPanel {
    #spanend
      public final JCheckBox[] buttons;
      public CheckBoxPanel(String[] t) {
        super();
        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        buttons = new JCheckBox[t.length];
        for (int i=0; i<buttons.length; i++) {
          add(buttons[i] = new JCheckBox(t[i]));
          });
        }
        InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), title[0]);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), title[1]);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), title[2]);
      }
    #spandel
    }
    #spanend
    #spandel
    enum User { OTHER, GROUP, OWNER; }
    #spanend
    #spandel
    class CheckBoxEditorRenderer extends AbstractCellEditor
    #spanend
          implements TableCellRenderer, TableCellEditor {
      protected final String[] title = {"owner", "group", "other"};
      protected final CheckBoxPanel editor = new CheckBoxPanel(title);
      protected final CheckBoxPanel renderer = new CheckBoxPanel(title);
      protected void updateButtons(CheckBoxPanel p, Object v) {
        @SuppressWarnings("unchecked")
        EnumSet<User> f = (v==null)? EnumSet.noneOf(User.class)
                          :(EnumSet<User>)v;
        p.buttons[0].setSelected(f.contains(User.OWNER));
        p.buttons[1].setSelected(f.contains(User.GROUP));
        p.buttons[2].setSelected(f.contains(User.OTHER));
      }
      @Override public Component getTableCellRendererComponent(JTable table,
          Object value, boolean isSelected, boolean hasFocus, int row, int col) {
        updateButtons(renderer, value);
        return renderer;
      }
    #spanadd
    
    #spanend
      @Override public Component getTableCellEditorComponent(JTable table,
          Object value, boolean isSelected, int row, int column) {
        updateButtons(editor, value);
        return editor;
            Object value, boolean isSelected, int row, int column) {
        updateButtons(value);
        return this;
      }
    #spanadd
    
    #spanend
      @Override public Object getCellEditorValue() {
        EnumSet<User> f = EnumSet.noneOf(User.class);
        if (editor.buttons[0].isSelected()) f.add(User.OWNER);
        if (editor.buttons[1].isSelected()) f.add(User.GROUP);
        if (editor.buttons[2].isSelected()) f.add(User.OTHER);
        return f;
        int i = 0;
        if (buttons[0].isSelected()) i |= 1 << 2;
        if (buttons[1].isSelected()) i |= 1 << 1;
        if (buttons[2].isSelected()) i |= 1 << 0;
        return i;
      }
    #spandel
    }
    #spanend
    #spanadd
    
    #spanend
      // Copied from AbstractCellEditor
      protected EventListenerList listenerList = new EventListenerList();
      transient protected ChangeEvent changeEvent = null;
      // ...
    

解説

  • JTableのセル内に3つのJCheckBoxを配置したJPanelを作成
    • このJPanelCellRenderer用とCellEditor用として別々に使用
  • JCheckBoxをマウスでクリックすると直下にあるJCheckBoxの選択状態のみ更新
  • 編集中にカラムヘッダの移動やリサイズ(JFrameなどのリサイズに連動)などが発生してもチェックした内容がリセットされないようにCellEditorのチェックボックスがクリックされたらfireEditingStopped()メソッドを呼び出して編集を終了し更新を確定する

参考リンク

コメント