Background
I am writing this GUI as a plugin for another program. The GUI prompts the user to enter values which should be returned to the main program when the window closes (in this case the println
statement in tester).
My current method is using an ActionListener on the okButton
, which when clicked, sets returnValue
to the text in the text field, and closes the window.
I have read through the docs and attempted to search terms such as "return value from GUI", "return field value after button press", but I have not yet found anything relating to my use case. I have minimal experience with Java GUI's, so there is a solid chance that I am missing a built-in way of doing this.
Driver class:
public class Tester {
public static void main(String[] args) {
TestWindow tw = new TestWindow();
String ret = tw.run();
System.out.println(ret);
}
}
Window Class
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class TestWindow {
JFrame jFrame = new JFrame();
JTextField textField = new JTextField("", 10);
JButton okButton = new JButton("OK");
String returnValue = "defaultValue";
public TestWindow() {
jFrame.setSize(400, 400);
textField.setBounds(50, 100, 200, 30);
okButton.setBounds(50, 150, 200, 30);
okButton.addActionListener(e -> {
// Set returnValue field to textField value
returnValue = textField.getText();
// Dispose of JFrame
jFrame.dispose();
});
jFrame.add(textField);
jFrame.add(okButton);
jFrame.setLayout(null);
}
public String run() {
jFrame.setVisible(true);
return textField.getText();
}
}
When I step through the driver class with a debugger, the value returned is always "defaultValue", or the initial default value of retValue
. Furthermore, the print statement after the run
method runs immediately as the window appears, not after the GUI exits.
Question
How can I give this value back to the main program when the OK button is clicked? (My actual program returns a hashmap of the various values set in the UI, but I have simplified it to a text field for purposes of a MCVE).
CodePudding user response:
Use a modal dialog, see How to Make Dialogs for more details.
As a simple example, you could just use JOptionPane
...
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JTextField textField = new JTextField(10);
textField.setText("Default value");
JPanel contentPane = new JPanel();
contentPane.add(new JLabel("Type something: "));
contentPane.add(textField);
JOptionPane.showMessageDialog(null, contentPane, "Type someting", JOptionPane.PLAIN_MESSAGE);
System.out.println(textField.getText());
}
});
The alternative would be to implement some kind of observer pattern which could be called when the ActionListener
is triggered, but to be honest, a JOptionPane
is just simpler
CodePudding user response:
I propose writing a minimal interface for a callback, such as:
interface StuffToDoWithDialog {
// will be called once values for foo and bar are ready
void doStuff(String foo, int bar);
}
and building the interface so that, once values are ready, the passed-in instance of this interface is called:
public static void showDialog(StuffToDoWithDialog callback) {
SwingUtilities.invokeLater(() -> {
// ... draw interface ...
// ... and somewhere, once values are ready, call callback
callback.doStuff(foo, bar);
});
}
this makes it very simple to test, and you can pass in much more complex behavior when needed:
public static void main(String ... args) {
showDialog((foo, bar) -> {
System.out.println("Foo is " foo);
System.out.println("Bar is " bar);
});
}
Working program (save as Example.java
):
import javax.swing.*;
import java.awt.*;
public class Example {
interface StuffToDoWithDialog {
void doStuff(String foo, int bar);
}
public static void main(String ... args) {
showDialog((foo, bar) -> {
System.out.println("Foo is " foo);
System.out.println("Bar is " bar);
});
}
private static JTextField addFormRowToPanel(JPanel panel, String label, int rowNum) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.ipadx = 2;
gbc.gridx = 0;
gbc.gridy = rowNum;
panel.add(new JLabel(label), gbc);
JTextField field = new JTextField();
field.setPreferredSize(new Dimension(50, (int)field.getPreferredSize().getHeight()));
gbc.gridx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(field, gbc);
return field;
}
public static void showDialog(StuffToDoWithDialog callback) {
SwingUtilities.invokeLater(() -> {
// builds contents for dialog: two inputs with labels
JPanel jp = new JPanel(new GridBagLayout());
JTextField foo = addFormRowToPanel(jp, "String foo", 0);
JTextField bar = addFormRowToPanel(jp, "Integer bar", 1);
boolean valid = false;
while ( ! valid) {
// blocks while showing dialog
JOptionPane.showMessageDialog(null,
jp, "Input options", JOptionPane.PLAIN_MESSAGE);
// checks results
if (foo.getText() != null
&& bar.getText() != null
&& bar.getText().matches("[0-9] ")) {
valid = true;
} else {
// complain: bad inputs
int rc = JOptionPane.showConfirmDialog(null,
"Invalid inputs, try again or cancel?", "Error",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE);
if (rc == JOptionPane.CANCEL_OPTION) return;
}
}
// returns results
callback.doStuff(foo.getText(), Integer.parseInt(bar.getText()));
});
}
}