概要

JComboBoxのセルレンダラとして選択状態用のJCheckBox、複数行文字列用のJTextAreaなどを配置したJPanelを使用し、異なる高さのセルを描画します。

サンプルコード

JComboBox<ComboItem> combo = new JComboBox<ComboItem>(model) {
  private transient PopupMenuListener listener;

  @Override public void updateUI() {
    removePopupMenuListener(listener);
    super.updateUI();
    setPrototypeDisplayValue(new ComboItem("**********", ""));
    setRenderer(new CheckComboBoxRenderer<>(this));
    // AccessibleContext ac = getAccessibleContext();
    // ComboPopup popup = (ComboPopup) ac.getAccessibleChild(0);
    // JList<?> list = popup.getList();
    // list.setFixedCellHeight(-1);
    listener = new WidePopupMenuListener();
    addPopupMenuListener(listener);
  }
};
View in GitHub: Java, Kotlin

解説

  • JComboBox#setPrototypeCellValue(...)JComboBox本体のサイズを指定
    • ドロップダウンリストで使用するJListのセルサイズには影響せず(自動的にJList#setPrototypeCellValue(...)は実行されない)、デフォルトのJList.setFixedCellHeight(-1)が適用されて各要素のセルレンダラにgetPreferredSizeを設定してListUIでセルの高さが計算される
    • JListで異なる高さのセルを使用
  • JComboBoxPopupMenuListenerを追加してドロップダウンリストの幅をJComboBox本体より広く設定
  • JComboBox#setRenderer(...)ListCellRenderer#getListCellRendererComponent(...)を実装したセルレンダラを設定
    • ドロップダウンリストのセルを描画する場合(index>=0)は、選択状態を表示するJCheckBox、タイトルを表示するJLabel、複数行のテキストを表示するJTextAreaなどをJPanelに配置して返す
    • JComboBox本体のセルを描画する場合(index==-1)は、タイトルを表示するJLabelを返す
class CheckComboBoxRenderer<E extends ComboItem>
    implements ListCellRenderer<E> {
  private static final Color SELECTED_BGC = new Color(0xC0_E8_FF);
  private final EditorPanel renderer;
  private final JLabel label = new JLabel();
  private final JComboBox<ComboItem> combo;

  protected CheckComboBoxRenderer(JComboBox<ComboItem> combo) {
    this.combo = combo;
    ComboItem proto = Optional
        .ofNullable(combo.getPrototypeDisplayValue())
        .orElseGet(() -> new ComboItem("", ""));
    renderer = new EditorPanel(proto);
  }

  @Override public Component getListCellRendererComponent(
      JList<? extends E> list, E value, int index,
      boolean isSelected, boolean cellHasFocus) {
    Component c;
    if (index >= 0) {
      renderer.setItem(value);
      if (isSelected) {
        renderer.setSelected(true);
        renderer.setOpaque(true);
        renderer.setBackground(SELECTED_BGC);
      } else {
        renderer.setSelected(combo.getSelectedIndex() == index);
        renderer.setOpaque(false);
        renderer.setBackground(Color.WHITE);
      }
      c = renderer;
    } else {
      label.setOpaque(false);
      label.setText(Objects.toString(value, ""));
      c = label;
    }
    return c;
  }
}

参考リンク

コメント