简体   繁体   English

JOptionPane-检查用户输入并防止关闭直到满足条件

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

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? 请问有人可以告诉我是否有一种简便的方法可以防止JOptionPane在单击“确定”后关闭,除非满足用户输入字段的条件?

Or do I have no choice but to use JFrame ? 还是我别无选择,只能使用JFrame

My validation logic so far. 到目前为止,我的验证逻辑。 Doesn't seem to work because the buttons are one-time clickable to some reason... 似乎不起作用,因为由于某种原因,这些按钮可以一次单击。

final JDialog dialog3 = new JDialog(OmniGUI.getFrame(), "Create new Node - id:" + newNodeID);
dialog3.setContentPane(theOPane);
dialog3.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);

theOPane.addPropertyChangeListener(new PropertyChangeListener(){
   public void propertyChange(PropertyChangeEvent e) {

       if(e.getSource() == theOPane){
           String val = (String) ((JOptionPane) e.getSource()).getValue();

           if(val=="Create"){
               System.out.println("Checking content");                      

               if(!valid){
                   System.out.println("closing the window");    

                   dialog3.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                   dialog3.removeAll();
                   dialog3.dispatchEvent(new WindowEvent(dialog3, WindowEvent.WINDOW_CLOSING));
               }

           }
       }
   }    
});

    dialog3.setLocation(p);
    dialog3.pack();
    dialog3.setVisible(true);

You can create your own Custom JDialog to check user input etc before closing or moving on. 您可以创建自己的Custom JDialog以在关闭或继续之前检查用户输入等。 See this link: 看到这个链接:

Stopping Automatic Dialog Closing 停止自动关闭对话框

By default, when the user clicks a JOptionPane-created button, the dialog closes. 默认情况下,当用户单击JOptionPane创建的按钮时,对话框关闭。 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: 如果键入错误/没有文本,然后单击输入,将显示验证消息:

在此处输入图片说明

If you click X to close Dialog or click Cancel a validation message will be shown also: 如果单击X关闭对话框或单击“取消”,还将显示一条验证消息:

在此处输入图片说明

If correct text is entered (in this case "David") and enter is clicked a message is shown and JDialog is exited: 如果输入正确的文本(在这种情况下为“ David”),然后单击enter,则会显示一条消息,并退出JDialog

在此处输入图片说明

CustomDialog.java: 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);
            }
        });
    }
}

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). 回想起来,我认为我第一次尝试不起作用的原因可能是因为它使用了JOptionPanel.createDialog()(而不是示例代码所做的事情)。 Maybe letting the JOptionPanel create it's own JDialog set up some "background" dependencies in how the event processing worked... meh. 也许让JOptionPanel创建它自己的JDialog,在事件处理的工作方式上设置了一些“后台”依赖项。 In any case, I've got what I wanted now: David Kroucamp's code was very useful to me. 无论如何,我现在都有了想要的东西:David Kroucamp的代码对我来说非常有用。

I'm posting my solution because it handles PropertyChangeEvents differently than David, so it might be useful to some people. 我发布我的解决方案是因为它与David的处理方式不同,因此它对某些人可能有用。 You'll see that much of the code is identical to his (thanks David) 您会看到很多代码与他的代码相同(感谢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. 它在用于验证用户输入的构造函数中需要一些args。 The validation is if(!Files.exists(rootPathArg.resolve(input))) { // close the dialog } 验证为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...
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM