概要

JComboBoxを編集可能に設定し、ComboBoxEditorとして複数のJCheckBoxJTextFieldを配置したJPanelを設定します。

サンプルコード

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) {
    editor.addActionListener(l);
  }

  @Override public void removeActionListener(ActionListener l) {
    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 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(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(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(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.setEnabled(enabledCheck.isSelected());
    data.setEditable(editableCheck.isSelected());
    data.setText(textField.getText());
    return data;
  }

  public void setItem(ComboItem item) {
    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);
  }
}
View in GitHub: Java, Kotlin

解説

  • setEditable(false), setRenderer(...)
    • 編集不可のJComboBoxにレンダラーとして複数のJCheckBoxJTextFieldを配置したJPanelを使用
    • 編集不可のJComboBoxやドロップダウンリストの描画にのみ(このためイベントなどは無効)レンダラーとして設定したJPanelが使用される
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にレンダラーとエディタとして複数のJCheckBoxJTextFieldを配置したJPanelを使用
    • 編集可能なJComboBoxのエディタとして設定したJPanelがそのまま使用されるためJCheckBoxなどに設定したActionListenerなども有効
      • JCheckBox: 右JCheckBoxJTextFieldsetEnabled(...)を切り替える
      • JCheckBox: JTextFieldsetEditable(...)を切り替える

参考リンク

コメント