Reputation: 177
I want to be able to use multiple JComboBoxes with a single common data source. I want the combo boxes to be able to display either an item from that list or a blank item, and most importantly I want them to not display an item if it is currently selected by another combo box (but show it if it become unselected).
I have been trying to go about this by calling a removeDuplicates() method which should add all the currently selected items to a list, remove that from the master list, and then set that as the list for the combo boxes.
This has been giving me some interesting problems. In my program none of the options from the master list show up, even initially, though they do if I get rid of my code to remove duplicates (the initialization is tested working).
Also even though they are editable combo boxes I cannot write a selected item to them, it gets deleted as soon as I press the enter button.
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
@SuppressWarnings("serial")
public class Main extends JFrame implements ActionListener
{
ArrayList<String> commonItemList = new ArrayList<String>(Arrays.asList("Dog", "Cat", "Fish", "Bear", "Lion"));
ArrayList<JComboBox<Object>> comboBoxes = new ArrayList<>();
public Main()
{
this.setSize(new Dimension(500, 150));
this.setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
JComboBox<Object> comboBox1 = new JComboBox<Object>(commonItemList.toArray());
comboBox1.setEditable(true);
comboBox1.addActionListener(this);
JComboBox<Object> comboBox2 = new JComboBox<Object>(commonItemList.toArray());
comboBox2.setEditable(true);
comboBox2.addActionListener(this);
JComboBox<Object> comboBox3 = new JComboBox<Object>(commonItemList.toArray());
comboBox3.setEditable(true);
comboBox3.addActionListener(this);
this.add(comboBox1);
comboBoxes.add(comboBox1);
this.add(comboBox2);
comboBoxes.add(comboBox2);
this.add(comboBox3);
comboBoxes.add(comboBox3);
}
public static void main(String[] args)
{
Main main = new Main();
main.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent arg0)
{
removeDuplicates();
}
private void removeDuplicates()
{
ArrayList<String> currentlyUsedItems = new ArrayList<>();
ArrayList<String> availableItems = commonItemList;
// Add all currently selected items to usedItems list
for (JComboBox<Object> comboBox : comboBoxes)
{
currentlyUsedItems.add((String) comboBox.getSelectedItem());
}
// For every string in currentlyUsedItems remove it from availableItems
for (String string : currentlyUsedItems)
{
availableItems.remove(string);
}
// Remove all items from combobox, then add back all available Items, while disabling actionListener
for (JComboBox<Object> comboBox : comboBoxes)
{
comboBox.removeActionListener(this);
comboBox.removeAllItems();
for (String string : availableItems)
{
comboBox.addItem(string);
}
comboBox.addActionListener(this);
}
}
}
Above I have posted a SSCCE, which does not have exactly the same issues as mine but it still bugged regardless, and uses the same method I used to attempt to solve my issue. Is there something I am missing or is this just a poor way to go about a solution?
Upvotes: 3
Views: 810
Reputation: 5415
One approach would be to simply "disable" items in the combo's drop-down list (i.e., make them unselectable) if they are already selected in another combobox.
Here's one way of disabling items in a JComboBox
(using Metal):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
public class JComboBoxDisabledItemsDemo implements ItemListener, Runnable
{
private static final String ITEMS[] =
{ "Black", "Blue", "Green", "Orange", "Purple", "Red", "White", "Yellow"};
private JComboBox jCombo;
private JCheckBox[] checkBoxes = new JCheckBox[ITEMS.length];
private boolean[] enabledFlags = new boolean[ITEMS.length];
public static void main(String args[])
{
SwingUtilities.invokeLater(new JComboBoxDisabledItemsDemo());
}
@SuppressWarnings("unchecked")
public void run()
{
JPanel pnlEnablers = new JPanel(new GridLayout(0,1));
pnlEnablers.setBorder(BorderFactory.createTitledBorder("Enabled Items"));
for (int i = 0; i < ITEMS.length; i++)
{
checkBoxes[i] = new JCheckBox(ITEMS[i]);
checkBoxes[i].setSelected(true);
checkBoxes[i].addItemListener(this);
enabledFlags[i] = true;
pnlEnablers.add(checkBoxes[i]);
}
jCombo = new JComboBox(ITEMS);
jCombo.setUI(new MetalComboBoxUI()
{
@Override
protected ComboPopup createPopup()
{
ComboPopup cp = new BasicComboPopup( comboBox )
{
@Override
protected void configureList()
{
super.configureList();
list.setSelectionModel(new DisabledItemSelectionModel());
}
};
return cp;
}
});
jCombo.setRenderer(new DisabledItemListCellRenderer());
JFrame f = new JFrame("Colors");
Container contentPane = f.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(pnlEnablers, BorderLayout.CENTER);
contentPane.add(jCombo, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(240, 280);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public void itemStateChanged(ItemEvent event)
{
JCheckBox checkBox = (JCheckBox) event.getSource();
int index = -1;
int selectedComboIndex = jCombo.getSelectedIndex();
for (int i = 0; i < ITEMS.length; i++)
{
if (ITEMS[i].equals(checkBox.getText()))
{
index = i;
break;
}
}
if (index != -1)
{
enabledFlags[index] = checkBox.isSelected();
jCombo.repaint();
if (index == selectedComboIndex)
{
jCombo.setSelectedIndex(-1);
}
}
}
public class DisabledItemListCellRenderer extends DefaultListCellRenderer
{
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus)
{
if (index < 0 || enabledFlags[index])
{
return super.getListCellRendererComponent(list, value, index,
isSelected, cellHasFocus);
}
Component comp = super.getListCellRendererComponent(list, value, index,
false, false);
comp.setEnabled(false);
return comp;
}
}
public class DisabledItemSelectionModel extends DefaultListSelectionModel
{
/**
* No need to override addSelectionInterval(int index0, int index1)
* since we're using SINGLE_SELECTION mode for this demo.
*/
@Override
public void setSelectionInterval(int index0, int index1)
{
if (enabledFlags[index0])
{
super.setSelectionInterval(index0, index0);
}
else
{
/*
* The previously selected index is before this one,
* so walk forward to find the next selectable item.
*/
if (getAnchorSelectionIndex() < index0)
{
for (int i = index0; i < enabledFlags.length; i++)
{
if (enabledFlags[i])
{
super.setSelectionInterval(i, i);
return;
}
}
}
/*
* Otherwise, walk backward to find the next selectable item.
*/
else
{
for (int i = index0; i >= 0; i--)
{
if (enabledFlags[i])
{
super.setSelectionInterval(i, i);
return;
}
}
}
}
}
}
}
Upvotes: 2