• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTableのCellにJCheckBoxを複数配置する
#navi(../)
RIGHT:Posted by [[aterai]] at 2011-02-28
*JTableのCellにJCheckBoxを複数配置する [#v80be979]
JTableのセル中にJCheckBoxを複数個配置します。

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

//#screenshot
#ref(https://lh4.googleusercontent.com/_9Z4BYR88imo/TWs6JY73P8I/AAAAAAAAA2M/wwrwT7R5K4k/s800/CheckBoxesInTableCell.png)

**サンプルコード [#p4dd25d7]
#code{{
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);
  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);
  }
  @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;
  }
}
}}

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

----
ヘッダカラムの移動、リサイズ(JFrameなどのリサイズ)で、チェックした内容が消えてしまわないように、JTable自体に以下の様なMouseListenerを追加してチェックボックスがクリックされるたびにtable.getCellEditor(row, col).stopCellEditing();を呼び出しています。

#code{{
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();
    }
  }
});
}}

JTableにではなく、CellEditor自体にMouseListenerを追加する方法もあります。
#code{{
class CheckBoxEditorRenderer2 extends CheckBoxEditorRenderer
                              implements MouseListener {
  private final JTable table;
  public CheckBoxEditorRenderer2(JTable table) {
    super();
    this.table = table;
    editor.addMouseListener(this);
  }
  //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;
    }
  }
  @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) {}
}
}}

**参考リンク [#wc856fe8]
-[[JTableのセル中にJRadioButtonを配置>Swing/RadioButtonsInTableCell]]
-[[JCheckBoxのセルをロールオーバーする>Swing/RolloverBooleanRenderer]]

**コメント [#xf4aaa77]
- EnumSetのテスト -- [[aterai]] &new{2011-03-01 (火) 14:22:06};
- ビットフラグをEnumSetに変更するテスト -- [[aterai]] &new{2011-03-01 (火) 14:22:06};
#code{{
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);
    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();
        }
      }
    });
    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 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;
  }
}
}}
-- [http://www.ne.jp/asahi/hishidama/home/tech/java/enum.html#h2_flag フラグとしての論理和(EnumSetの例) - Java列挙型メモ(Hishidama's Java enum Memo)] -- [[aterai]] &new{2011-03-01 (火) 21:12:24};

#comment