I have read the doc and tutorial, and searchd here, to no avail.
Oracle tutorial: how to use custom render for ComboBox
Another question similar with a somehow vague answer
And I see it important because many people asked about it but no one can provide a simple, workable example. So I must ask it myself:
How can we make a combobox with a drop-down menu, allowing us to choose more than one options?
What is not working:
JList
proved to be useless here, because I cannot make it appear in the drop-down menu. CheckBoxList
in Swing.I have done a SCCEE with checkbox in drop-down menu of a combo, but the checkboxes refuse to be selected, the check in the box is missing.
How can we achieve that?
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.List;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
public class ComboOfCheckBox extends JFrame {
public ComboOfCheckBox() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
JTable table = new JTable(new Object[2][2], new String[]{"COL1", "COL2"});
final JCheckBox chx1 = new JCheckBox("Oh");
final JCheckBox chx2 = new JCheckBox("My");
final JCheckBox chx3 = new JCheckBox("God");
String[] values = new String[] {"Oh", "My", "God"};
JCheckBox[] array = new JCheckBox[] {chx1, chx2, chx3};
final JComboBox<JCheckBox> comboBox = new JComboBox<JCheckBox>(array) {
@Override
public void setPopupVisible(boolean visible){
if (visible) {
super.setPopupVisible(visible);
}
}
};
class CheckBoxRenderer implements ListCellRenderer {
private boolean[] selected;
private String[] items;
public CheckBoxRenderer(String[] items) {
this.items = items;
this.selected = new boolean[items.length];
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
JLabel label = null;
JCheckBox box = null;
if (value instanceof JCheckBox) {
label = new JLabel(((JCheckBox)value).getText());
box = new JCheckBox(label.getText());
}
return box;
}
public void setSelected(int i, boolean selected) {
this.selected[i] = selected;
}
}
comboBox.setRenderer(new CheckBoxRenderer(values));
panel.add(comboBox);
panel.add(new JCheckBox("Another"));
getContentPane().add(panel);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ComboOfCheckBox frame = new ComboOfCheckBox();
}
});
}
}
Here's a partial answer to go with. It doesn't address the issue of the ComboBox masking events on the popup, but it does work around it. The problem is still that the ComboBox treats each select on one item as a deselect on another. However, one problem you were facing is that, since the renderer is called every time upon repaint, your CheckBoxes weren't persistent - the Map
addresses that.
public class ComboOfCheckBox extends JFrame {
public ComboOfCheckBox() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
JTable table = new JTable(new Object[2][2], new String[]{"COL1", "COL2"});
String[] values = new String[] {"Oh", "My", "God"};
final JComboBox<String> comboBox = new JComboBox<String>(values) {
@Override
public void setPopupVisible(boolean visible){
if (visible) {
super.setPopupVisible(visible);
}
}
};
class CheckBoxRenderer implements ListCellRenderer<Object> {
private Map<String, JCheckBox> items = new HashMap<>();
public CheckBoxRenderer(String[] items) {
for (String item : items) {
JCheckBox box = new JCheckBox(item);
this.items.put(item, box);
}
}
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
if (items.containsKey(value)) {
return items.get(value);
} else {
return null;
}
}
public void setSelected(String item, boolean selected) {
if (item.contains(item)) {
JCheckBox cb = items.get(item);
cb.setSelected(selected);
}
}
}
final CheckBoxRenderer renderer = new CheckBoxRenderer(values);
comboBox.setRenderer(renderer);
comboBox.addItemListener(e -> {
String item = (String) e.getItem();
if (e.getStateChange() == ItemEvent.DESELECTED) {
renderer.setSelected(item, false);
} else {
renderer.setSelected(item, true);
}
});
panel.add(comboBox);
panel.add(new JCheckBox("Another"));
getContentPane().add(panel);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ComboOfCheckBox frame = new ComboOfCheckBox();
}
});
}
}