问题
I have a little gui program that on startup reads data from an Excel file and some of these data need to go to the relevant comboboxes. I know how to do this by using a separate SwingWorker for each combobox:
public class ExcelReader extends SwingWorker<DefaultComboBoxModel, String> {
private final DefaultComboBoxModel model;
// Constructor called from a panel that has a combobox
public ExcelReader(DefaultComboBoxModel model) {
this.model = model;
}
@Override
protected DefaultComboBoxModel doInBackground() throws Exception {
///// code to read data from Excel file
///// publish(someString) used here
return model;
}
@Override
protected void process(List<String> chunks) {
for(String row : chunks)
model.addElement(row);
}
}
This works fine, but how can I use one SwingWorker class to fill multiple comboboxes? This would have the benefit of reading the file just once. Whenever something that needs to go to a combobox is found the relevant combobox is updated and then the program continues reading the next row until the end of the file.
So I tried to use boolean flags for the case that I want to update 2 comboboxes in one JPanel but this doesn't seem to work as expected. It's also not a good solution because in the future I'm planning to update more than 2 comboboxes in more than one panels.
public class ExcelReader extends SwingWorker<DefaultComboBoxModel[], String> {
private boolean isData1 = false;
private final DefaultComboBoxModel model1;
private final DefaultComboBoxModel model2;
private final DefaultComboBoxModel[] models = new DefaultComboBoxModel[2];
// Constructor called from a panel that has 2 comboboxes
public ExcelReader(DefaultComboBoxModel model1, DefaultComboBoxModel model2) {
this.model1 = model1;
this.model2 = model2;
}
@Override
protected DefaultComboBoxModel[] doInBackground() throws Exception {
///// some code .....
///// some code .....
// If data that needs to go to combobox1 is found
if (someExpression)
isData1 = true;
publish(someString);
///// some code .....
///// some code .....
models[0] = model1;
models[1] = model2;
return models;
}
@Override
protected void process(List<String> chunks) {
for (String row : chunks) {
if (isData1) {
model1.addElement(row);
isData1 = false;
}
else
model2.addElement(row);
}
}
So how can only one SwingWorker be used to fill many comboboxes (that might be contained in different panels)?
By the way for the above examples I'm calling ExcelReader from one of my panels (class that extends JPanel). In the first case the panel calling the worker has only one combobox, the second has 2. While the first example works fine, is this the correct way to update the comboboxes in a gui or should the worker be called from somewhere else?
回答1:
- Map your models to logical names, so you can attach data from excel to a logical part of the UI.
- Pass to the worker the UI elements you'd like to interact with (the models, in this case)
- Go off the UI thread ASAP (in this case the action) and let the
SwingWorker
sync the UI updates for you. - Let the
SwingWorker
batch updates for you, while mapping them to a logical function in your UI.
The codez:
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class TestPanel extends JPanel {
private TestPanel() {
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
Map<String, DefaultComboBoxModel> map = new HashMap<String, DefaultComboBoxModel>();
for (int i = 0; i < 5; i++) {
JComboBox combo = new JComboBox();
add(combo);
map.put("combo" + i, (DefaultComboBoxModel) combo.getModel());
}
add(new JButton(new AbstractAction("fill me") {
@Override
public void actionPerformed(ActionEvent e) {
new MyWorker(map).run();
}
}));
}
private class MyWorker extends SwingWorker<Void, String[]> {
private final Map<String, DefaultComboBoxModel> map;
public MyWorker(Map<String, DefaultComboBoxModel> map) {
this.map = map;
}
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < 20; i++) {
String[] cell = new String[2];
cell[0] = "combo" + i % 5;
cell[1] = "value " + i;
publish(cell);
}
return null;
}
@Override
protected void process(List<String[]> chunks) {
for (String[] chunk : chunks) {
map.get(chunk[0]).addElement(chunk[1]);
}
}
};
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("SwingWorker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new TestPanel();
frame.setContentPane(panel);
// Display the window.
frame.setSize(200, 300);
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
}
来源:https://stackoverflow.com/questions/12028016/swingworker-updating-multiple-comboboxes-in-multilpe-panels