概要
JComboBox
のアイテムとしてJCheckBox
を表示し、ドロップダウンリストを開いたままこれを複数選択可能に設定します。
Screenshot
Advertisement
サンプルコード
class CheckedComboBox<E extends CheckableItem> extends JComboBox<E> {
private boolean keepOpen;
private transient ActionListener listener;
protected CheckedComboBox() {
super();
}
protected CheckedComboBox(ComboBoxModel<E> aModel) {
super(aModel);
}
protected CheckedComboBox(E[] m) {
super(m);
}
@Override public Dimension getPreferredSize() {
return new Dimension(200, 20);
}
@Override public void updateUI() {
setRenderer(null);
removeActionListener(listener);
super.updateUI();
listener = e -> {
if ((e.getModifiers() & AWTEvent.MOUSE_EVENT_MASK) != 0) {
updateItem(getSelectedIndex());
keepOpen = true;
}
};
setRenderer(new CheckBoxCellRenderer<CheckableItem>());
addActionListener(listener);
getInputMap(JComponent.WHEN_FOCUSED).put(
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "checkbox-select");
getActionMap().put("checkbox-select", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
Accessible a = getAccessibleContext().getAccessibleChild(0);
if (a instanceof BasicComboPopup) {
BasicComboPopup pop = (BasicComboPopup) a;
updateItem(pop.getList().getSelectedIndex());
}
}
});
}
private void updateItem(int index) {
if (isPopupVisible()) {
E item = getItemAt(index);
item.selected ^= true;
setSelectedIndex(-1);
setSelectedItem(item);
}
}
@Override public void setPopupVisible(boolean v) {
if (keepOpen) {
keepOpen = false;
} else {
super.setPopupVisible(v);
}
}
public static <E extends CheckableItem> String getCheckedItemString(
ListModel<E> model) {
return IntStream.range(0, model.getSize())
.mapToObj(model::getElementAt)
.filter(CheckableItem::isSelected)
.map(Objects::toString)
.sorted()
.collect(Collectors.joining(", "));
}
}
View in GitHub: Java, Kotlin解説
- タイトルと選択状態をもつアイテムオブジェクト
CheckableItem
を作成し、そのモデルとしてComboBoxModel<CheckableItem>
を作成 CheckBoxCellRenderer<E extends CheckableItem>
を作成し、チェック状態を表示JComboBox
本体: レンダラーにJLabel
を使用し選択されているCheckableItem
のタイトルを収集してカンマで結合して一覧表示- ドロップダウンリスト: レンダラーに
JCheckBox
を使用しチェック状態とタイトルを表示
JComboBox
にActionListener
を追加し、マウスの左クリックかつドロップダウンリストが表示されている場合は選択されたアイテムのチェック状態を反転- この場合は、ドロップダウンリストを閉じないように
JComboBox#setPopupVisible(...)
をオーバーライド
- この場合は、ドロップダウンリストを閉じないように
- Spaceキーでアイテムが選択された場合は、
BasicComboPopup
からJList
を取得してその選択アイテムを取得する- この場合
JComboBox#getSelectedIndex()
などを使用するとハイライト(cellHasFocus
)されているアイテムではなく選択状態(isSelected
)のアイテムが取得される
- この場合
Ubuntu 20.04.2 LTS
環境でドロップダウンリストの再描画がおかしくなる場合がある- 以下のようにセルレンダラーを
DefaultListCellRenderer
、クリックイベントの取得をMouseListener
に変更すると解消される?
- 以下のようにセルレンダラーを
Accessible a = getAccessibleContext().getAccessibleChild(0);
if (a instanceof ComboPopup) {
((ComboPopup) a).getList().addMouseListener(new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
JList<?> list = (JList<?>) e.getComponent();
if (SwingUtilities.isLeftMouseButton(e)) {
keepOpen = true;
updateItem(list.locationToIndex(e.getPoint()));
}
}
});
}
DefaultListCellRenderer renderer = new DefaultListCellRenderer();
JCheckBox check = new JCheckBox();
check.setOpaque(false);
setRenderer((list, value, index, isSelected, cellHasFocus) -> {
panel.removeAll();
Component c = renderer.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (index < 0) {
String txt = getCheckedItemString(list.getModel());
JLabel l = (JLabel) c;
l.setText(txt.isEmpty() ? " " : txt);
l.setOpaque(false);
l.setForeground(list.getForeground());
panel.setOpaque(false);
} else {
check.setSelected(value.isSelected());
panel.add(check, BorderLayout.WEST);
panel.setOpaque(true);
panel.setBackground(c.getBackground());
}
panel.add(c);
return panel;
});