I am new to Java and Java Swing but it is what I inherited. I am trying to figure out the best way to keep a combobox up to date with a changing list. If the user has already selected an item and that item is still in the new list I want to keep it selected. The following code seems to work but I'm looking for suggestions o make it better, cleaner, etc. especially in the "done" method of the swing worker. Currently I am dealing with the combobox and the model (I thought the model alone would be enough).
In my example the list of cars will be the same first 4 and the last 5-7 are randomly added or removed.
class CBItem {
private int id;
private String description;
public CBItem(int id, String description) {
this.id = id;
this.description = description;
}
public int getId() {
return id;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return description;
}
@Override
public boolean equals(Object o) {
boolean equal = true;
if (!(o instanceof CBItem)) {
equal = false;
}
else {
CBItem other = (CBItem)o;
equal = other.description.equals(this.description);
}
return equal;
}
}
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
public class JComboBoxFiller extends JFrame {
private static final long serialVersionUID = 1L;
JPanel pnlMain;
DefaultComboBoxModel<CBItem> mdlCars = new DefaultComboBoxModel<CBItem>();
List<CBItem> lstCurrentCars = new ArrayList<CBItem>();
JComboBox<CBItem> cbxCars;
final String[] arrCarNames = new String[] {"Audi", "BMW", "Chevrolet", "Dodge", "Ford", "Hyundai", "Jaguar"};
public JComboBoxFiller() {
setTitle("ComboBox Filler Demo");
setBounds(100, 100, 400, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pnlMain = new JPanel(new BorderLayout());
setContentPane(pnlMain);
cbxCars = new JComboBox<CBItem>(mdlCars);
JPanel pnlCenter = new JPanel();
pnlCenter.add(cbxCars, BorderLayout.CENTER);
pnlMain.add(pnlCenter, BorderLayout.CENTER);
setVisible(true);
// Every 5 seconds repopulate the list of cars
Timer timer = new Timer(3000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new RepopulateCarsWorker().execute();
}
});
timer.start();
}
// This will be called every 5 seconds. It populates a list of 4 to 7 cars (CBItems)
// then updates the combobox (cbxCars) with the new values.
// It needs to keep the current car selected (if one was and still exists)
private class RepopulateCarsWorker extends SwingWorker<ArrayList<CBItem>, Void>
{
ArrayList<CBItem> lstNewCars = new ArrayList<CBItem>();
protected ArrayList<CBItem> doInBackground()
{
Random rand = new Random();
int lastCar = 4 + rand.nextInt(3);
for (int c = 0; c < lastCar; c++) {
lstNewCars.add(new CBItem(c, arrCarNames[c]));
}
return lstNewCars;
}
protected void done()
{
try
{
ArrayList<CBItem> lstNewCars = get();
// If list stayed the same, nothing to do
if (lstNewCars.equals(lstCurrentCars)) {
return;
}
// Save the current selection so it can be reselected
CBItem cbCurrentSelection = (CBItem) mdlCars.getSelectedItem();
mdlCars.removeAllElements();
mdlCars.addAll(lstNewCars);
// Reselect if there was a selected item and it still exists
// Question: Why do I have to deal with the combobox?
// I thought model would be enough...
mdlCars.setSelectedItem(cbCurrentSelection);
cbxCars.setSelectedIndex(mdlCars.getIndexOf(cbCurrentSelection));
// Update current list for next comparison
lstCurrentCars = lstNewCars;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JComboBoxFiller frame = new JComboBoxFiller();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Why do I have to deal with the combobox? I thought model would be enough...
I agree, updating the model should cause the view to be updated.
Your code is always setting the selected item which may be causing the problem.
I modified your code to clear the selected item when it is not found in the model and you don't need to reference the combo box:
// Save the current selection so it can be reselected
CBItem cbCurrentSelection = (CBItem) mdlCars.getSelectedItem();
// Reselect if there was a selected item and it still exists
// Question: Why do I have to deal with the combobox?
// I thought model would be enough...
mdlCars.removeAllElements();
mdlCars.addAll(lstNewCars);
if (mdlCars.getIndexOf(cbCurrentSelection) == -1)
mdlCars.setSelectedItem(null);
else
mdlCars.setSelectedItem(cbCurrentSelection);
//cbxCars.setSelectedIndex(mdlCars.getIndexOf(cbCurrentSelection));
// Update current list for next comparison
lstCurrentCars = lstNewCars;