---
category: swing
folder: CheckableComboBoxEditor
title: JComboBoxのComboBoxEditorに編集可能なJCheckBoxを追加する
tags: [JComboBox, ComboBoxEditor, JCheckBox]
author: aterai
pubdate: 2018-11-05T16:25:10+09:00
description: JComboBoxを編集可能に設定し、ComboBoxEditorとして複数のJCheckBoxとJTextFieldを配置したJPanelを設定します。
image: https://drive.google.com/uc?id=1f3vhPqkXg-Jg6AK0p01Pn7aGRTo-Wlv0pw
---
* 概要 [#summary]
`JComboBox`を編集可能に設定し、`ComboBoxEditor`として複数の`JCheckBox`と`JTextField`を配置した`JPanel`を設定します。

#download(https://drive.google.com/uc?id=1f3vhPqkXg-Jg6AK0p01Pn7aGRTo-Wlv0pw)

* サンプルコード [#sourcecode]
#code(link){{
class CheckComboBoxEditor implements ComboBoxEditor {
  private final EditorPanel editor = new EditorPanel(new ComboItem());
  @Override public void selectAll() {
    editor.selectAll();
  }
  @Override public Object getItem() {
    return editor.getItem();
  }
  @Override public void setItem(Object anObject) {
    EventQueue.invokeLater(() -> {
      Container c = SwingUtilities.getAncestorOfClass(
          JComboBox.class, getEditorComponent());
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        int idx = combo.getSelectedIndex();
        if (idx >= 0 && idx != editor.getEditingIndex()) {
          System.out.println("setItem: " + idx);
          editor.setEditingIndex(idx);
        }
      }
    });
    if (anObject instanceof ComboItem) {
      editor.setItem((ComboItem) anObject);
    } else {
      editor.setItem(new ComboItem());
    }
  }
  @Override public Component getEditorComponent() {
    return editor;
  }
  @Override public void addActionListener(ActionListener l) {
    System.out.println("addActionListener: " + l.getClass().getName());
    editor.addActionListener(l);
  }
  @Override public void removeActionListener(ActionListener l) {
    System.out.println("removeActionListener: " + l.getClass().getName());
    editor.removeActionListener(l);
  }
}

final class EditorPanel extends JPanel {
  private final JCheckBox enabledCheck = new JCheckBox();
  private final JCheckBox editableCheck = new JCheckBox();
  private final JTextField textField = new JTextField("", 16);
  private final ComboItem data;
  private final transient ComboItem data;
  private int editingIndex = -1;

  protected EditorPanel(ComboItem data) {
    super();
    this.data = data;
    setItem(data);

    enabledCheck.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        ComboItem item = (ComboItem) combo.getItemAt(data.getIndex());
        ComboItem item = (ComboItem) combo.getItemAt(editingIndex);
        item.setEnabled(((JCheckBox) e.getSource()).isSelected());
        editableCheck.setEnabled(item.isEnabled());
        textField.setEnabled(item.isEnabled());
        combo.setSelectedIndex(editingIndex);
      }
    });
    enabledCheck.setOpaque(false);
    enabledCheck.setFocusable(false);

    editableCheck.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        ComboItem item = (ComboItem) combo.getItemAt(data.getIndex());
        ComboItem item = (ComboItem) combo.getItemAt(editingIndex);
        item.setEditable(((JCheckBox) e.getSource()).isSelected());
        textField.setEditable(item.isEditable());
        combo.setSelectedIndex(editingIndex);
      }
    });
    editableCheck.setOpaque(false);
    editableCheck.setFocusable(false);

    textField.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        ComboItem item = (ComboItem) combo.getItemAt(data.getIndex());
        ComboItem item = (ComboItem) combo.getItemAt(editingIndex);
        item.setText(((JTextField) e.getSource()).getText());
        combo.setSelectedIndex(editingIndex);
      }
    });
    textField.setBorder(BorderFactory.createEmptyBorder());
    textField.setOpaque(false);

    setOpaque(false);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(enabledCheck);
    add(editableCheck);
    add(textField);
  }
  public int getEditingIndex() {
    return editingIndex;
  }
  public void setEditingIndex(int idx) {
    this.editingIndex = idx;
  }
  public ComboItem getItem() {
    // data.index(this.data.index);
    data.setEnabled(enabledCheck.isSelected());
    data.setEditable(editableCheck.isSelected());
    data.setText(textField.getText());
    return data;
  }
  public void setItem(ComboItem item) {
    data.setIndex(item.getIndex());

    enabledCheck.setSelected(item.isEnabled());

    editableCheck.setSelected(item.isEditable());
    editableCheck.setEnabled(item.isEnabled());

    textField.setText(item.getText());
    textField.setEnabled(item.isEnabled());
    textField.setEditable(item.isEditable());
  }
  public void selectAll() {
    textField.requestFocusInWindow();
    textField.selectAll();
  }
  public void addActionListener(ActionListener l) {
    textField.addActionListener(l);
    enabledCheck.addActionListener(l);
    editableCheck.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    textField.removeActionListener(l);
    enabledCheck.removeActionListener(l);
    editableCheck.removeActionListener(l);
  }
}
}}

* 解説 [#explanation]
- `setEditable(false), setRenderer(...)`
-- 編集不可の`JComboBox`にレンダラーとして複数の`JCheckBox`と`JTextField`を配置した`JPanel`を使用
-- 編集不可の`JComboBox`やドロップダウンリストの描画にのみ(このためイベントなどは無効)レンダラーとして設定した`JPanel`が使用される

#code{{
class CheckComboBoxRenderer<E extends ComboItem> implements ListCellRenderer<E> {
  private static final Color SBGC = new Color(100, 200, 255);
  private final EditorPanel renderer = new EditorPanel(new ComboItem());
  @Override public Component getListCellRendererComponent(
      JList<? extends E> list, E value, int index,
      boolean isSelected, boolean cellHasFocus) {
    renderer.setItem(value);
    if (isSelected && index >= 0) {
      renderer.setOpaque(true);
      renderer.setBackground(SBGC);
    } else {
      renderer.setOpaque(false);
      renderer.setBackground(Color.WHITE);
    }
    return renderer;
  }
}
}}

- `setEditable(true), setRenderer(...), setEditor(...)`
-- 編集可能な`JComboBox`にレンダラーとエディタとして複数の`JCheckBox`と`JTextField`を配置した`JPanel`を使用
-- 編集可能な`JComboBox`のエディタとして設定した`JPanel`がそのまま使用されるため、`JCheckBox`などに設定した`ActionListener`なども有効
--- 左`JCeckBox`: 右`JCeckBox`と`JTextField`の`setEnabled(...)`を切り替える
--- 右`JCeckBox`: `JTextField`の`setEditable(...)`を切り替える

* 参考リンク [#reference]
- [[JComboBoxのEditorComponentにJButtonを配置>Swing/ButtonInComboEditor]]
- [[JComboBoxのアイテムとして表示したJCheckBoxを複数選択する>Swing/CheckedComboBox]]
- [https://stackoverflow.com/questions/53037906/using-the-comboboxeditor-interface-with-custom-jcomponent-and-allow-edit-and-d java - Using the ComboBoxEditor interface with Custom JComponent, and allow edit, and display the List - Stack Overflow]

* コメント [#comment]
#comment
#comment