I'm trying to implement a function showTexfieldSettingsDialouge(Map fields) in a little 'code generator'-application, taking a Hashmap full of Strings as input, showing a swing window with iteratively created panels and text-fields, for every key mentioned in the Hashmap, and returning this Hashmap filled with the inputs by the user, after clicking a button.
The Problem: To wait for the button to be clicked, I have to create an ActionListener, which runs in a new (the Swing-)Thread.
How can I let the function wait for the ActionListener to be fired, to go on, write the input an then return?
For better help I may post the code I wrote until now:
public class GUIMacroHandler {
//diverse other functions
public HashMap showTexfieldSettingsDialouge(Map fields){
JFrame mainFrame = new JFrame("Please fill every Textfield with the correct Data");
Map labels = new HashMap();
Map textfields = new HashMap();
JPanel optionpanel = new JPanel(new GridLayout(fields.size(), 2, 10, 10));
JSplitPane splitPane = new JSplitPane();
JButton btnSubmit = new JButton("Submit");
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
mainFrame.getContentPane().add(splitPane, BorderLayout.NORTH);
splitPane.setTopComponent(optionpanel);
splitPane.setBottomComponent(btnSubmit);
for (Entry entry : fields.entrySet()) {
labels.put(entry.getKey(),new JLabel(entry.getKey() + ":"));
textfields.put(entry.getKey(),new JTextField(entry.getValue());
optionpanel.add(labels.get(entry.getKey()));
optionpanel.add(textfields.get(entry.getKey()));
}
btnSubmit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Can't do stuff here because the function needs to return after the button is clicked and not before
}
});
mainFrame.pack();
mainFrame.setVisible(true);
//TODO:
//Wait for button to be clicked somehow
//Fill the given values with the input of the text fields
return fields;
}
}
The function is cast more than once in the code and is thought to easily change any given String/String HashMap by user wanted values.
f.e. like so:
GUIMacroHandler guiMacroHandler = new GUIMacroHandler();
combatsettings = guiMacroHandler.showTexfieldSettingsDialouge(combatsettings);
I've googled a whole time an didn't found a solution for my problem, I would be really happy for help, thanks in advance! :D
Edit:
Just post the stuff in the Action performed statement would only be a possible solution if I would use this function only once in the code, but not here because the function is forced to wait for the button to be clicked to return In the actual code the gui window would get created with the inputs of the Hashmap an then return the given Hashmap and the code would go on with no user input, the window would still exist, waiting for the button to be clicked to eventually expire, but no use for user changed data and code would go on with the defaults, because it doesn't waits for the users input.
解决方案
You can do something like that if you're sure that you're calling this method from some thread != the EDT. For example like so
public static String getFrameResult() throws InterruptedException {
if (SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Can't be called from EDT.");
}
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(1);
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Hello");
JButton button = new JButton("Done");
button.addActionListener((ActionEvent e) -> {
String result = "Done at : " + LocalDateTime.now();
queue.offer(result);
frame.dispose();
});
frame.add(button);
frame.pack();
frame.setVisible(true);
});
return queue.take();
}
The trick here is to store the result in memory and to notify the calling thread that it has been saved so it can take it and return it. A BlockingQueue works well, since Java 8 a CompletableFuture or even manually via wait/notify and a shared variable.