• category: swing folder: CheckBoxCellEditor title: JTableのセル内部にあるJCheckBoxのみクリック可能にする tags: [JTable, JCheckBox, TableCellEditor, JPanel] author: aterai pubdate: 2015-07-20T03:24:10+09:00 description: JTableのセルエディタとしてJPanel中央に配置したJCheckBoxを設定し、そのJCheckBoxをクリックした場合のみ選択状態が変化するように設定します。 image: https://lh3.googleusercontent.com/-t1TDt_cSOzU/VavlaZDRBaI/AAAAAAAAN9c/nVCdYcC71e8/s800-Ic42/CheckBoxCellEditor.png

概要

JTableのセルエディタとしてJPanel中央に配置したJCheckBoxを設定し、そのJCheckBoxをクリックした場合のみ選択状態が変化するように設定します。

サンプルコード

class CheckBoxPanelEditor extends AbstractCellEditor implements TableCellEditor {
  private final JPanel p = new JPanel(new GridBagLayout());
  private final JCheckBox checkBox = new JCheckBox();
  public CheckBoxPanelEditor() {
    super();
    checkBox.setOpaque(false);
    checkBox.setFocusable(false);
    checkBox.setRolloverEnabled(false);
    Handler handler = new Handler();
    checkBox.addActionListener(handler);
    checkBox.addMouseListener(handler);
    p.addMouseListener(new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        fireEditingStopped();
      }
    });
    p.add(checkBox);
    p.setBorder(UIManager.getBorder("Table.noFocusBorder"));
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    checkBox.setSelected(Objects.equals(value, Boolean.TRUE));
    //p.setBackground(table.getSelectionBackground());
    return p;
  }
  @Override public Object getCellEditorValue() {
    return checkBox.isSelected();
  }
  private class Handler extends MouseAdapter implements ActionListener {
    @Override public void actionPerformed(ActionEvent e) {
      fireEditingStopped();
    }
    @Override public void mousePressed(MouseEvent e) {
      Container c = SwingUtilities.getAncestorOfClass(JTable.class, e.getComponent());
      if (c instanceof JTable) {
        JTable table = (JTable) c;
        if (checkBox.getModel().isPressed()
            && table.isRowSelected(table.getEditingRow()) && e.isControlDown()) {
          p.setBackground(table.getBackground());
        } else {
          p.setBackground(table.getSelectionBackground());
        }
      }
    }
    @Override public void mouseExited(MouseEvent e) {
      Container c = SwingUtilities.getAncestorOfClass(JTable.class, e.getComponent());
      if (c instanceof JTable) {
        JTable table = (JTable) c;
        if (table.isEditing() && !table.getCellEditor().stopCellEditing()) {
          table.getCellEditor().cancelCellEditing();
        }
      }
    }
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JTableBooleanに対応するTableCellEditorとして、中央にJCheckBoxを配置したJPanelを適用しています。

  • デフォルトのJTable.BooleanEditor
    • セル全体がJCheckBoxになるため、チェックアイコン以外の余白をクリックしても選択状態が変化する
  • CheckBoxPanelEditor
    • JPanelの中央にJCheckBoxを配置して作成
    • 余白となるJPanel部分をクリックしても、JCheckBoxの状態は変化しない
    • JCheckBoxをマウスでクリックした場合のみ、その選択状態が変化する

  • 以下のようにgetTableCellEditorComponent(...)内でJPanelの背景色をセルの選択色にする場合、Ctrlキーを押しながらJCheckBoxをクリックするとセルの選択状態にズレが発生する
    • このサンプルでは、JCheckBoxにマウスリスナーを追加することで回避している
class CheckBoxPanelEditor extends AbstractCellEditor implements TableCellEditor {
  private final JPanel p = new JPanel(new GridBagLayout());
  private final JCheckBox checkBox = new JCheckBox();
  public CheckBoxPanelEditor() {
    super();
    checkBox.setOpaque(false);
    checkBox.setFocusable(false);
    checkBox.setRolloverEnabled(false);
    checkBox.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
      }
    });
    p.add(checkBox);
    p.setBorder(UIManager.getBorder("Table.noFocusBorder"));
  }
  @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    checkBox.setSelected(Objects.equals(value, Boolean.TRUE));
    p.setBackground(table.getSelectionBackground());
    return p;
  }
  @Override public Object getCellEditorValue() {
    return checkBox.isSelected();
  }
}

参考リンク

コメント