JOptionPane - check user input and prevent from closing until conditions are met

前端 未结 2 1856
萌比男神i
萌比男神i 2020-11-28 12:12

Please can someone tell me if there is a convenient way of prevent JOptionPane from closing upon clicking OK unless the conditions for user input fields are met

相关标签:
2条回答
  • 2020-11-28 12:57

    You can create your own Custom JDialog to check user input etc before closing or moving on. See this link:

    Stopping Automatic Dialog Closing

    By default, when the user clicks a JOptionPane-created button, the dialog closes. But what if you want to check the user's answer before closing the dialog? In this case, you must implement your own property change listener so that when the user clicks a button, the dialog does not automatically close.

    Here is an example I made:

    If you type wrong/no text and click Enter a validation message will be displayed:

    enter image description here

    If you click X to close Dialog or click Cancel a validation message will be shown also:

    enter image description here

    If correct text is entered (in this case "David") and enter is clicked a message is shown and JDialog is exited:

    enter image description here

    CustomDialog.java:

    import java.awt.*;
    import java.awt.event.*;
    import java.beans.*;
    import javax.swing.JDialog;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    class CustomDialog extends JDialog
            implements ActionListener,
            PropertyChangeListener {
    
        private String typedText = null;
        private JTextField textField;
        private String magicWord;
        private JOptionPane optionPane;
        private String btnString1 = "Enter";
        private String btnString2 = "Cancel";
    
        /**
         * Returns null if the typed string was invalid; otherwise, returns the
         * string as the user entered it.
         */
        public String getValidatedText() {
            return typedText;
        }
    
        /**
         * Creates the reusable dialog.
         */
        public CustomDialog(Frame aFrame, String aWord) {
            super(aFrame, true);
    
            magicWord = aWord.toUpperCase();
            setTitle("Quiz");
    
            textField = new JTextField(10);
    
            //Create an array of the text and components to be displayed.
            String msgString1 = "What was Dr. SEUSS's real last name?";
            String msgString2 = "(The answer is \"" + magicWord
                    + "\".)";
            Object[] array = {msgString1, msgString2, textField};
    
            //Create an array specifying the number of dialog buttons
            //and their text.
            Object[] options = {btnString1, btnString2};
    
            //Create the JOptionPane.
            optionPane = new JOptionPane(array,
                    JOptionPane.QUESTION_MESSAGE,
                    JOptionPane.YES_NO_OPTION,
                    null,
                    options,
                    options[0]);
    
            //Make this dialog display it.
            setContentPane(optionPane);
    
            //Handle window closing correctly.
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    
            //Ensure the text field always gets the first focus.
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentShown(ComponentEvent ce) {
                    textField.requestFocusInWindow();
                }
            });
    
            //Register an event handler that puts the text into the option pane.
            textField.addActionListener(this);
    
            //Register an event handler that reacts to option pane state changes.
            optionPane.addPropertyChangeListener(this);
            pack();
        }
    
        /**
         * This method handles events for the text field.
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            optionPane.setValue(btnString1);
        }
    
        /**
         * This method reacts to state changes in the option pane.
         */
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            String prop = e.getPropertyName();
    
            if (isVisible()
                    && (e.getSource() == optionPane)
                    && (JOptionPane.VALUE_PROPERTY.equals(prop)
                    || JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
                Object value = optionPane.getValue();
    
                if (value == JOptionPane.UNINITIALIZED_VALUE) {
                    //ignore reset
                    return;
                }
    
                //Reset the JOptionPane's value.
                //If you don't do this, then if the user
                //presses the same button next time, no
                //property change event will be fired.
                optionPane.setValue(
                        JOptionPane.UNINITIALIZED_VALUE);
    
                if (btnString1.equals(value)) {
                    typedText = textField.getText();
                    String ucText = typedText.toUpperCase();
                    if (magicWord.equals(ucText)) {
                        JOptionPane.showMessageDialog(this, "Correct answer given");
                        exit();
                    } else {
                        //text was invalid
                        textField.selectAll();
                        JOptionPane.showMessageDialog(this,
                                "Sorry, \"" + typedText + "\" "
                                + "isn't a valid response.\n"
                                + "Please enter "
                                + magicWord + ".",
                                "Try again",
                                JOptionPane.ERROR_MESSAGE);
                        typedText = null;
                        textField.requestFocusInWindow();
                    }
                } else { //user closed dialog or clicked cancel
                    JOptionPane.showMessageDialog(this, "It's OK.  "
                            + "We won't force you to type "
                            + magicWord + ".");
                    typedText = null;
                    exit();
                }
            }
        }
    
        /**
         * This method clears the dialog and hides it.
         */
        public void exit() {
            dispose();
        }
    
        public static void main(String... args) {
            //create JDialog and components on EDT
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new CustomDialog(null, "David").setVisible(true);
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-28 13:06

    One thing about Stop Automatic Dialog Closing is that it is only helps if you want to prevent closing or validate and then close... basing a solution on the sample code in that tutorial, I could not get it to validate and stay open if the validation failed.

    In retrospect, I think the reason my first attempt did not work may be because it used JOptionPanel.createDialog() (not what the example code did). Maybe letting the JOptionPanel create it's own JDialog set up some "background" dependencies in how the event processing worked... meh. In any case, I've got what I wanted now: David Kroucamp's code was very useful to me.

    I'm posting my solution because it handles PropertyChangeEvents differently than David, so it might be useful to some people. You'll see that much of the code is identical to his (thanks David)

    This class checks for file existance and lets the user provide a new name or cancel. It takes some args in the constructor that it uses to validate users' input. The validation is if(!Files.exists(rootPathArg.resolve(input))) { // close the dialog }

    class GetPathNameDialog extends JDialog implements ActionListener, PropertyChangeListener {
    
        /**
         * contains the users input
         */
        private JTextField textField;
        /**
         * the option pane that holds all fields and controls in this dialog
         */
        private JOptionPane optionPane;
        /**
         * label for the left button that represents "OK"
         */
        private String button1Str;
        /**
         * label for the right button that represents "Cancel"
         */
        private String button2Str;
        /**
         * path containing the named entity to be renamed.
         */
        private Path rootPath;
    
        /**
         * Creates the reusable dialog.
         */
        /**
         * Creates the dialog, panel and all GUI components, sets up listeners.
         *
         * @param rootPath the path where the file or folder we are renaming lives
         * @param initialText the initial text to display in the text field (i.e. current name)
         * @param title title of the JDialog itself
         * @param textFieldWidth number of columns in the JTextField that will contain the file/folder name
         * @param promptStr the propmt to display in the panel
         * @param button1Str the label for the "OK" button
         * @param button2Str the label for the "Cancel" button
         */
        public GetPathNameDialog(Path rootPath, String initialText, String title, int textFieldWidth, String promptStr, String button1Str, String button2Str) {
    
            super((Frame) null, true);
    
            // init class variables
            this.rootPath = rootPath;
            this.button1Str = button1Str;
            this.button2Str = button2Str;
    
            setTitle(title);
    
            textField = new JTextField(textFieldWidth);
            textField.setText(initialText);
    
            //Create an array of the text and components to be displayed.
            Object[] array = {promptStr, textField};
    
            //Create an array specifying the number of dialog buttons
            //and their text.
            Object[] options = {button1Str, button2Str};
    
            //Create the JOptionPane.
            optionPane = new JOptionPane(
                    array,
                    JOptionPane.QUESTION_MESSAGE,
                    JOptionPane.YES_NO_OPTION,
                    null,
                    options,
                    options[0]);
    
            //Make this dialog display it.
            setContentPane(optionPane);
    
            //Handle window closing correctly.
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    
            //Ensure the text field always gets the first focus.
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentShown(ComponentEvent ce) {
                    textField.requestFocusInWindow();
                }
            });
    
            // Register an event handler that puts the text into the option pane INPUT_VALUE_PROPERTY
            textField.addActionListener(this);
    
            // Register an event handler that reacts to option pane state changes.
            optionPane.addPropertyChangeListener(this);
    
            // tell this dialog to display close to the current mouse pointer
            setLocation(MouseInfo.getPointerInfo().getLocation());
            pack();
        }
    
        /**
         * This method handles events for the text field.
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            // this will fire a INPUT_VALUE_PROPERTY PropertyChangeEvent... takes the user input to the validaton code in the property handler
            optionPane.setInputValue(textField.getText());
        }
    
        /**
         * This method reacts to property changes.
         */
        @Override
        public void propertyChange(PropertyChangeEvent e) {
    
            String prop = e.getPropertyName();
    
            if (isVisible() && (e.getSource() == optionPane)) {
    
                // the VALUE_PROPERTY is not the same as the INPUT_VALUE_PROPERTY. we make use of the INPUT_VALUE_PROPERTY to carry our data
                // but the JOptionPane uses the VALUE_PROPERTY for other stuff
                if (JOptionPane.VALUE_PROPERTY.equals(prop)) {
                    // newValues delivered by VALUE_PROPERTY PropertyChangeEvent can be the actual labels of the button clicked,
                    // that's sooo counter-intuitive to me, but it's how we know which button got clicked
                    if (button1Str.equals(e.getNewValue())) {
                        // "OK" functionality...
                        // ...this will fire the event that takes the user input to the validation code
                        optionPane.setInputValue(textField.getText());
                    } else if (button2Str.equals(e.getNewValue())) {
                        // "CANCEL" functionality
                        optionPane.setInputValue(null);
                        exit();
                    }
    
                } else if (JOptionPane.INPUT_VALUE_PROPERTY.equals(prop)) {
    
                    Object value = optionPane.getInputValue();
    
                    // null or empty strings in the text field (ie in the INPUT_VALUE_PROPERTY) are ignored
                    if (null != value && ((String) value).length() > 0) {
                        // here is the validation code
                        if (Files.exists(rootPath.resolve(textField.getText()))) {
                            // already exists, tell user
                            JOptionPane.showMessageDialog(this,
                                    "Sorry, " + rootPath.resolve(textField.getText()).toString() + " already exists.\n\n Please enter another name.",
                                    "OK",
                                    JOptionPane.ERROR_MESSAGE);
                            // Make sure PropertyChangeEvent will fire next time...
                            // ...PropertyChangeEvents don't fire in setInputValue(newVal)...
                            // ...if newVal is equal to the current value, but if the user clicks...
                            // ...button 1 or hits enter in the text field without changing his text,...
                            // ...we still want to fire another event...
                            // ...so we reset the property without changing the text in the textField
                            optionPane.setInputValue(null);
                        } else {
                            // does not exist.. we are keeping the users input...
                            // ... it gets delivered to the user in getInputValue()
                            exit();
                        }
                    }
                }
            }
        }
    
        /**
         * returns the users's validated input. Validated means !Files.exists(rootPath.resolve(input)).
         *
         * @return the text entered by the user, UNINITIALIZED_VALUE if the user X closed, null the user canceled
         */
        public Object getInputValue() {
            return optionPane.getInputValue();
        }
    
        /**
         * closes the dialog and triggers the return from setVisible()
         */
        public void exit() {
            dispose();
        }
    }
    

    The code to invoke it is:

        GetPathNameDialog tempD = new GetPathNameDialog(
                            someFolderPath,
                            "theFileNameThatMustBeChanged.txt",
                            "Change File Name",
                            50,
                            "someFolderPath already contains a file named theFileNameThatMustBeChanged.txt." + ".\n\nPlease enter a different file name:",
                            "Copy the file with the new name", "Do not copy the file");
        tempD.setVisible(true);
    
        Object inputObj = tempD.getInputValue();
        String input = (inputObj == JOptionPane.UNINITIALIZED_VALUE || null == inputObj ? "" : (String) inputObj);
    
        if (input.length() > 0) {
            // we now have a new file name. go ahead and do the copy or rename or whatever...
        }
    
    0 讨论(0)
提交回复
热议问题