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

Posted by aterai at 2011-02-28

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

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

  • &jnlp;
  • &jar;
  • &zip;
CheckBoxesInTableCell.png

サンプルコード

class CheckBoxEditorRenderer extends AbstractCellEditor
      implements TableCellRenderer, TableCellEditor {
  protected final String[] title = {"r", "w", "x"};
  protected final CheckBoxPanel editor = new CheckBoxPanel(title);
  protected final CheckBoxPanel renderer = new CheckBoxPanel(title);
  public CheckBoxEditorRenderer() {
    ActionListener al = new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
      }
    };
    for(AbstractButton b: editor.buttons) b.addActionListener(al);
  }
  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);
  }
  class CheckBoxPanel extends JPanel {
    public final JCheckBox[] buttons;
    public CheckBoxPanel(String[] t) {
      super();
      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
      setOpaque(false);
      buttons = new JCheckBox[t.length];
      ActionMap am = getActionMap();
      for(int i=0; i<buttons.length; i++) {
        final JCheckBox b = new JCheckBox(t[i]);
        b.setOpaque(false);
        b.setFocusable(false);
        b.setRolloverEnabled(false);
        b.setBackground(new Color(0,0,0,0));
        buttons[i] = b;
        add(b);
        am.put(t[i], new AbstractAction(t[i]) {
          public void actionPerformed(ActionEvent e) {
            b.setSelected(!b.isSelected());
            fireEditingStopped();
          }
        });
      }
      InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), t[0]);
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), t[1]);
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), t[2]);
    }
  }
  @Override public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row, int col) {
    updateButtons(renderer, value);
    return renderer;
  }
  @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;
  }
}

解説

上記のサンプルでは、JTableのCell内に3つのJCheckBoxを配置したJPanelを作成し、これをCellRendererとCellEditorとして別々に使用しています。JCheckBoxをマウスでクリックすると、そのJCheckBoxの選択状態だけが変化します(注: キーボードからの入力には対応していません)。


ヘッダカラムの移動、リサイズ(JFrameなどのリサイズ)で、チェックした内容が消えてしまわないように、CellEditorのチェックボックスがクリックされたらfireEditingStopped()メソッドを呼び出して編集を終了し更新を確定するようにしています。

JTable自体に以下の様なMouseListenerを追加してチェックボックスがクリックされるたびにtable.getCellEditor(row, col).stopCellEditing();を呼び出しています。

参考リンク

コメント

  • ビットフラグをEnumSetに変更するテスト -- aterai
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.table.*;
    public class EnumSetTest {
      public JComponent makeUI() {
        String[] columnNames = { "rwx", "user" };
        Object[][] data = {
          {"read",    EnumSet.of(User.OWNER, User.GROUP, User.OTHER)},
          {"write",   EnumSet.of(User.OWNER)},
          {"execute", EnumSet.noneOf(User.class)}
        };
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {
          @Override public Class<?> getColumnClass(int column) {
            return getValueAt(0, column).getClass();
          }
        };
        JTable table = new JTable(model);
        CheckBoxEditorRenderer cer = new CheckBoxEditorRenderer();
        TableColumn c = table.getColumnModel().getColumn(1);
        c.setCellRenderer(cer);
        c.setCellEditor(cer);
        c.setPreferredWidth(180);
    
        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);
      }
    }
    class CheckBoxPanel extends JPanel {
      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]));
        }
      }
    }
    enum User { OTHER, GROUP, OWNER; }
    class CheckBoxEditorRenderer extends AbstractCellEditor
          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 final ActionListener al = new ActionListener() {
        @Override public void actionPerformed(ActionEvent e) {
          fireEditingStopped();
        }
      };
      public CheckBoxEditorRenderer() {
        for(AbstractButton b: editor.buttons) b.addActionListener(al);
      }
      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;
      }
      @Override public Component getTableCellEditorComponent(JTable table,
          Object value, boolean isSelected, int row, int column) {
        updateButtons(editor, value);
        return editor;
      }
      @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;
      }
    }
    
  • rwxセル選択中にキーボードからr,w,xを入力するとチェックが切り替わるようにInputMap, ActionMapを追加。 -- aterai