JOptionPane如何不尊重同步?

时间:2017-08-16 18:46:22

标签: java swing event-handling joptionpane

我有一个系统,我需要能够问一个简单的"继续?"风格问题。有一个文本字段和一个按钮。要么单独修改文本字段,要么单独按下按钮,我需要能够要求用户确认。因此,这为我提供了文本字段的焦点监听器和按钮上的动作监听器。

但是,如果我编辑该字段,并立即单击该按钮,将弹出两个确认对话框 - 一个来自焦点监听器,另一个来自动作监听器后面的一个。现在,我显然只需要确认一次,所以我尝试同步抛出JOptionPane的函数,因此它不会输入两次,但这似乎没有效果。

一些代码证明了这一点:

public class Main extends JFrame {
    static public void main(String [] args) {
        SwingUtilities.invokeLater(() -> {
            Main m = new Main();
            m.setVisible(true);
        });
    }

    private JTextField field;
    private JButton button;

    private boolean isValid;

    public Main() {
        isValid = false;

        setLayout(new BorderLayout());

        field = new JTextField(15);
        field.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent arg0) {}

            @Override
            public void focusLost(FocusEvent e) {
                doValidate();
            }
        });
        add(field, BorderLayout.CENTER);

        button = new JButton("Push Me");
        button.addActionListener((ActionEvent e) -> {
            doValidate();
            doAction();
        });
        add(button, BorderLayout.SOUTH);

        pack();
    }

    private synchronized void doValidate() {
        System.out.println("Validating");
        if(!isValid) {
            int answer = JOptionPane.showConfirmDialog(this, "Really do it?");
            if(answer == JOptionPane.YES_OPTION)
                isValid = true;
        }
    }

    private void doAction() {
        System.out.println("Action done!");
    }
}

首先,这有点令人惊讶。如果有人对Swing / JOptionPane如何绕过synchronized关键字进行解释,我会很高兴。

至于如何处理,我的想法是我需要实现一个等待确认对话框答案的线程。然后,我需要检查线程是否处于活动状态(如果对话框已经启动,那么),如果它已启动,我需要在给出答案时添加一个监听器。我已经完成了这项任务,但感觉就像过度设计问题的解决方案一样,特别是因为我对这为什么会发生这种情况的理解有限。此外,我已经可以想象至少有几种不同的情况,我最终会遇到麻烦,使其更容易出错。

=== EDIT ===

要清楚,这些消息框会同时弹出,而不是按顺序弹出。例如:

Two panes

2 个答案:

答案 0 :(得分:1)

同步意味着2个线程不能同时进入块,而不是永远。他们只是等待其他线程释放对该方法的锁定然后调用它。因此你的2个对话框。

这可能无论如何都不相关,因为我认为UI渲染是相同的线程,所以你只是从两个独立的UI事件中调用两次相同的方法(我可能错了)。

至于让它工作,不要担心线程,而是查看DocumentListener

答案 1 :(得分:0)

我建议添加以下内容,以便单击红色x将退出程序。

setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);   //new

请注意,只需删除按钮监听器即可获得所需的行为。

以下是我认为可以为您提供所需行为的代码:

package swingdemo1;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class SwingDemo1 extends JFrame {
    static public void main(String [] args) {
        SwingUtilities.invokeLater(() -> {
            SwingDemo1 m = new SwingDemo1();
            m.setVisible(true);
        });
    }

    private JTextField field;
    private JButton button;

    private boolean isValid;
    private boolean validationDone;
    String state;

    public SwingDemo1() {

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);   //new

        isValid = false;

        setLayout(new BorderLayout());

        field = new JTextField(15);
        field.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent arg0) {

            }
            @Override
            public void focusLost(FocusEvent e) {               
                    doValidate();
                    doAction();                                    
            }
        });
        add(field, BorderLayout.CENTER);

        button = new JButton("Push Me");
        /*                                                            //new
        button.addActionListener((ActionEvent e) -> {
            System.out.println("ActionEvent: " + e.getActionCommand());              
                doValidate();
                doAction();
            });
        */                                                            //new
        add(button, BorderLayout.SOUTH);

        pack();
    }

    private synchronized void doValidate() {
        System.out.println("Validating");
        if(!isValid) {
            int answer = JOptionPane.showConfirmDialog(this, "Really do it?");
            if(answer == JOptionPane.YES_OPTION) 
                isValid = true;
        }        
    }

    private void doAction() {
        System.out.println("Action done!");
    }
}