Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/396.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当焦距被JOptionPane窃取时,JButton保持按下状态_Java_Swing_Jtextfield_Joptionpane_Inputverifier - Fatal编程技术网

Java 当焦距被JOptionPane窃取时,JButton保持按下状态

Java 当焦距被JOptionPane窃取时,JButton保持按下状态,java,swing,jtextfield,joptionpane,inputverifier,Java,Swing,Jtextfield,Joptionpane,Inputverifier,我刚开始荡秋千,我有一种情况。我正在设计一个基于xml文件输入(元数据)动态呈现GUI组件的应用程序。现在,出于验证目的,我的大多数JTextField都设置了InputVerifier。每当有无效输入时,输入验证器就会弹出JOptionPane 现在,如果用户输入一个无效数据并继续前进并单击面板上的按钮,则会弹出一个对话框,用户必须对此做出响应。但在此之后,按钮也不会绘制到释放状态。它看起来还是被压着的,但实际上不是。由于整个代码相当混乱,我将问题场景放在下面的代码中:- 我应该怎么做才能使J

我刚开始荡秋千,我有一种情况。我正在设计一个基于xml文件输入(元数据)动态呈现GUI组件的应用程序。现在,出于验证目的,我的大多数JTextField都设置了InputVerifier。每当有无效输入时,输入验证器就会弹出JOptionPane

现在,如果用户输入一个无效数据并继续前进并单击面板上的按钮,则会弹出一个对话框,用户必须对此做出响应。但在此之后,按钮也不会绘制到释放状态。它看起来还是被压着的,但实际上不是。由于整个代码相当混乱,我将问题场景放在下面的代码中:-

我应该怎么做才能使JButton看起来没有压力?如果能解释一下逻辑,我将不胜感激

提前谢谢

package test;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

public class VerifierTest extends JFrame {

    private static final long serialVersionUID = 1L;

    public VerifierTest() {
        JTextField tf;
        tf = new JTextField("TextField1");

        getContentPane().add(tf, BorderLayout.NORTH);
        tf.setInputVerifier(new PassVerifier());

        final JButton b = new JButton("Button");
        b.setVerifyInputWhenFocusTarget(true);
        getContentPane().add(b, BorderLayout.EAST);
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (b.hasFocus())
                    System.out.println("Button clicked");
            }
        });

        addWindowListener(new MyWAdapter());
    }

    public static void main(String[] args) {
        Frame frame = new VerifierTest();
        frame.setSize(400, 200);
        frame.setVisible(true);
        //frame.pack();
    }

    class MyWAdapter extends WindowAdapter {

        public void windowClosing(WindowEvent event) {
            System.exit(0);
        }
    }

    class PassVerifier extends InputVerifier {

        public boolean verify(JComponent input) {
            JTextField tf = (JTextField) input;
            String pass = tf.getText();
            if (pass.equals("Manish"))
                return true;
            else {
                String message = "illegal value: " + tf.getText();
                JOptionPane.showMessageDialog(tf.getParent(), message,
                        "Illegal Value", JOptionPane.ERROR_MESSAGE);

                return false;
            }
        }
    }
}

方法
verify
实际上不是打开JOptionPane的好地方

有几种方法可以解决你的问题:

  • 您希望每当textfield失去焦点且输入不正确时,都会出现此JOptionPane:在JTextField上使用FocusListener并对适当的事件采取行动
  • 您希望每次按下按钮时都显示此JOptionPane:如果输入不正确,请使用ActionListener
  • 下面是后一个选项的一小部分:

    import java.awt.BorderLayout;
    import java.awt.Frame;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.InputVerifier;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    
    public class VerifierTest extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        public VerifierTest() {
            final JTextField tf = new JTextField("TextField1");
    
            getContentPane().add(tf, BorderLayout.NORTH);
            tf.setInputVerifier(new PassVerifier());
    
            final JButton b = new JButton("Button");
            b.setVerifyInputWhenFocusTarget(true);
            getContentPane().add(b, BorderLayout.EAST);
            b.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!tf.getInputVerifier().verify(tf)) {
                        JOptionPane.showMessageDialog(tf.getParent(), "illegal value: " + tf.getText(), "Illegal Value",
                                JOptionPane.ERROR_MESSAGE);
                    }
                    if (b.hasFocus()) {
                        System.out.println("Button clicked");
                    }
                }
            });
            setDefaultCloseOperation(EXIT_ON_CLOSE);
        }
    
        public static void main(String[] args) {
            Frame frame = new VerifierTest();
            frame.setSize(400, 200);
            frame.setVisible(true);
        }
    
        class PassVerifier extends InputVerifier {
    
            @Override
            public boolean verify(JComponent input) {
                final JTextField tf = (JTextField) input;
                String pass = tf.getText();
                return pass.equals("Manish");
            }
        }
    }
    

    还考虑设置JFrice的默认关闭操作,而不是添加窗口侦听器(但是如果您想弹出一个对话框询问用户是否确定要退出应用程序,这是一个使用Windows监听器的好方法)。

    < P>我添加了一个调用“代码> SWIVITUTIOS,以确保GUI在事件线程上,我删除了你对框架的引用

    GUI在Windows XP上为我工作

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    import javax.swing.InputVerifier;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class VerifierTest implements Runnable {
    
        private static final long serialVersionUID = 1L;
    
        public VerifierTest() {
    
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame();
            frame.setSize(400, 200);
    
            JTextField tf;
            tf = new JTextField("TextField1");
            tf.setInputVerifier(new PassVerifier());
            frame.getContentPane().add(tf, BorderLayout.NORTH);
    
            final JButton b = new JButton("Button");
            b.setVerifyInputWhenFocusTarget(true);
            frame.getContentPane().add(b, BorderLayout.EAST);
            b.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (b.hasFocus())
                        System.out.println("Button clicked");
                }
            });
    
            frame.addWindowListener(new MyWAdapter());
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
           SwingUtilities.invokeLater(new VerifierTest());
        }
    
        class MyWAdapter extends WindowAdapter {
            @Override
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            }
        }
    
        class PassVerifier extends InputVerifier {
            @Override
            public boolean verify(JComponent input) {
                JTextField tf = (JTextField) input;
                String pass = tf.getText();
                if (pass.equals("Manish"))
                    return true;
                else {
                    String message = "illegal value: " + tf.getText();
                    JOptionPane.showMessageDialog(tf.getParent(), message,
                            "Illegal Value", JOptionPane.ERROR_MESSAGE);
    
                    return false;
                }
            }
        }
    }
    

    我已经为按钮添加了一个新的鼠标侦听器,如下所示,它现在对我来说似乎运行良好,但我不确定这是否是纠正按钮选择状态的好方法

    package test;
    
    import java.awt.BorderLayout;
    import java.awt.Frame;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    import javax.swing.InputVerifier;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    import javax.swing.plaf.basic.BasicButtonListener;
    
    public class VerifierTest extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        public VerifierTest() {
            JTextField tf;
            tf = new JTextField("TextField1");
    
            getContentPane().add(tf, BorderLayout.NORTH);
            tf.setInputVerifier(new PassVerifier());
    
            final JButton b = new JButton("Button");
            b.setVerifyInputWhenFocusTarget(true);
            getContentPane().add(b, BorderLayout.EAST);
            b.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (b.hasFocus())
                        System.out.println("Button clicked");
                }
            });
    
            b.addMouseListener(new BasicButtonListener(b) {
                @Override
                public void mouseExited(MouseEvent e) {
                    ((JButton)e.getSource()).getModel().setArmed(false);
                    ((JButton)e.getSource()).getModel().setPressed(false);
                }
    
            });
    
            addWindowListener(new MyWAdapter());
        }
    
        public static void main(String[] args) {
            Frame frame = new VerifierTest();
            frame.setSize(400, 200);
            frame.setVisible(true);
            // frame.pack();
        }
    
        class MyWAdapter extends WindowAdapter {
    
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            }
        }
    
        class PassVerifier extends InputVerifier {
    
            public boolean verify(JComponent input) {
                JTextField tf = (JTextField) input;
                String pass = tf.getText();
                if (pass.equals("Manish"))
                    return true;
                else {
                    final String message = "illegal value: " + tf.getText();
                            JOptionPane.showMessageDialog(null, message,
                                    "Illegal Value", JOptionPane.ERROR_MESSAGE);
    
                    return false;
                }
            }
        }
    }
    
    第一:在verify()中打开对话框的InputVerifier的所有实现都是无效的。他们违反了合同,API文件:

    这种方法应该没有副作用

    用“应该”真的意思是“不得”。副作用发生的正确位置是应该专注于治疗

    第二:将副作用(显示消息对话框)正确地移动到shouldYieldFocus中也不起作用。。。因为一个,那已经超过十年了

    作为一个围绕着一个bug的黑客,@Daurerdrem的鼠标听筒是任何可行的黑客都能得到的最好的工具:-)

    更新

    在玩了一点不同的选项来破解这个bug之后,这里有另一个破解-它和所有破解一样脆弱(并且无法通过LAF切换,如果需要动态切换,必须重新安装)

    要破解鼠标行为,基本方法是挂接ui安装的侦听器:

    • 找到原件
    • 实现一个自定义侦听器,将大多数事件直接委托给原始侦听器
    • 对于按下的事件,首先请求焦点:如果已将委托给原始,则不执行任何操作
    最后一个项目稍微复杂一些,因为焦点事件可以是异步的,所以我们必须调用检查以获得焦点。反过来,调用需要发送一个版本,以防没有人反对

    另一个怪癖是rootPane的按下操作(对于它的defaultButton):通过无条件调用doClick,它在不尊重任何InputVerifier的情况下完成。可以通过挂接到操作中进行黑客攻击,其模式与挂接到mouseListener中的模式相同:

    • 查找根窗格的已按下操作
    • 实现一个自定义操作,检查是否有可能被否决的inputVerifier:委托给原始的,否则什么也不做
    该示例按照以下思路进行了修改:

    public class VerifierTest implements Runnable {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        public void run() {
            InteractiveTestCase.setLAF("Win");
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 200);
    
            JTextField tf = new JTextField("TextField1");
            tf.setInputVerifier(new PassVerifier());
            frame.add(tf, BorderLayout.NORTH);
    
            final JButton b = new JButton("Button");
            frame.add(b);
            b.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                   System.out.println("Button clicked");
                }
            });
            // hook into the mouse listener
            replaceBasicButtonListener(b);
            frame.add(new JTextField("not validating, something else to focus"),
                    BorderLayout.SOUTH);
            frame.getRootPane().setDefaultButton(b);
            // hook into the default button action
            Action pressDefault = frame.getRootPane().getActionMap().get("press");
            frame.getRootPane().getActionMap().put("press", new DefaultButtonAction(pressDefault));
            frame.setVisible(true);
        }
    
        protected void replaceBasicButtonListener(AbstractButton b) {
            final BasicButtonListener original = getButtonListener(b);
            if (original == null) return;
            Hacker l = new Hacker(original);
            b.removeMouseListener(original);
            b.addMouseListener(l);
        }
    
        public static class Hacker implements MouseListener {
            private BasicButtonListener original;
    
            /**
             * @param original the listener to delegate to.
             */
            public Hacker(BasicButtonListener original) {
                this.original = original;
            }
    
            /**
             * Hook into the mousePressed: first request focus and
             * check its success before handling it.
             */
            @Override
            public void mousePressed(final MouseEvent e) {
                if (SwingUtilities.isLeftMouseButton(e)) {
                    if(e.getComponent().contains(e.getX(), e.getY())) {
                        // check if we can get the focus
                        e.getComponent().requestFocus();
                        invokeHandleEvent(e);
                        return;
                    }
                }
                original.mousePressed(e);
            }
    
            /**
             * Handle the pressed only if we are focusOwner.
             */
            protected void handlePressed(final MouseEvent e) {
                if (!e.getComponent().hasFocus())  {
                    // something vetoed the focus transfer
                    // do nothing
                    return;
                } else {
                    original.mousePressed(e);
                    // need a fake released now: the one from the
                    // original cycle might never has reached us
                    MouseEvent released = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_RELEASED,
                            e.getWhen(), e.getModifiers(), 
                            e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
                            );
                    original.mouseReleased(released);
                }
            }
    
    
            /**
             * focus requests might be handled
             * asynchronously. So wrap the check 
             * wrap the block into an invokeLater.
             */
            protected void invokeHandleEvent(final MouseEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        handlePressed(e);
                    }
                });
            }
    
            @Override
            public void mouseClicked(MouseEvent e) {
                original.mouseClicked(e);
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
                original.mouseReleased(e);
            }
    
            @Override
            public void mouseEntered(MouseEvent e) {
                original.mouseEntered(e);
            }
    
            @Override
            public void mouseExited(MouseEvent e) {
                original.mouseExited(e);
            }
        }
        public static class DefaultButtonAction extends AbstractAction {
    
            private Action original;
    
            /**
             * @param original
             */
            public DefaultButtonAction(Action original) {
                this.original = original;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                JRootPane root = (JRootPane) e.getSource();
                JButton owner = root.getDefaultButton();
                if (owner != null && owner.getVerifyInputWhenFocusTarget()) {
                    Component c = KeyboardFocusManager
                            .getCurrentKeyboardFocusManager()
                             .getFocusOwner();
                    if (c instanceof JComponent && ((JComponent) c).getInputVerifier() != null) {
                        if (!((JComponent) c).getInputVerifier().shouldYieldFocus((JComponent) c)) return;
                    }
    
    
                }
                original.actionPerformed(e);
            }
    
        }
        /**
         * Returns the ButtonListener for the passed in Button, or null if one
         * could not be found.
         */
        private BasicButtonListener getButtonListener(AbstractButton b) {
            MouseMotionListener[] listeners = b.getMouseMotionListeners();
    
            if (listeners != null) {
                for (MouseMotionListener listener : listeners) {
                    if (listener instanceof BasicButtonListener) {
                        return (BasicButtonListener) listener;
                    }
                }
            }
            return null;
        }
    
        public static void main(String[] args) {
           SwingUtilities.invokeLater(new VerifierTest());
        }
    
    
        public static class PassVerifier extends InputVerifier {
            /**
             * Decide whether or not the input is valid without
             * side-effects.
             */
            @Override
            public boolean verify(JComponent input) {
                final JTextField tf = (JTextField) input;
                String pass = tf.getText();
                if (pass.equals("Manish"))
                    return true;
                return false;
            }
    
            /**
             * Implemented to ask the user what to do if the input isn't valid.
             * Note: not necessarily the best usability, it's mainly to
             * demonstrate the different effects on not/agreeing with
             * yielding focus transfer.
             */
            @Override
            public boolean shouldYieldFocus(final JComponent input) {
                boolean valid = super.shouldYieldFocus(input);
                if (!valid) {
                    String message = "illegal value: " + ((JTextField) input).getText();
                    int goAnyWay = JOptionPane.showConfirmDialog(input, "invalid value: " +
                            message + " - go ahead anyway?");
                    valid = goAnyWay == JOptionPane.OK_OPTION;
                }
                return valid;
            }
        }
    }
    

    实际上,真正的问题在于焦点系统和awt侦听器如何交互。Java中声明了一些bug,开发人员反复讨论谁应该负责。 鼠标侦听器执行:ProcessMouseeEvent,在该逻辑中,当前FocusOwner被要求生成焦点。它失败了。但是,由于一半的事件已经处理完毕,因此按钮将处于待命状态,焦点仍保留在字段中

    我终于看到一条开发人员的评论:如果不允许字段失去焦点,就不要让侦听器继续

    例如: 定义一个JTextfield,编辑后只允许值小于100。 当你失去注意力时,会弹出一条信息。 我重写了我的基本JButton类的processMouseEvent(MouseEvent e) 代码为:

    protected void processMouseEvent(MouseEvent e) {
        if ( e.getComponent() != null && e.getComponent().isEnabled() ) { //should not be processing mouse events if it's disabled.
                if (e.getID() == MouseEvent.MOUSE_RELEASED && e.getClickCount() == 1) {
                    // The mouse button is being released as per normal, and it's the first click. Process it as per normal.
                    super.processMouseEvent(e);
    
                    // If the release occured within the bounds of this component, we want to simulate a click as well
                    if (this.contains(e.getX(), e.getY())) {
                        super.processMouseEvent(new MouseEvent(e.getComponent(),
                                                                MouseEvent.MOUSE_CLICKED,
                                                                e.getWhen(),
                                                                e.getModifiers(),
                                                                e.getX(),
                                                                e.getY(),
                                                                e.getClickCount(),
                                                                e.isPopupTrigger(),
                                                                e.getButton()));
                    }
                }
                else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 1) {
                    // Normal clicks are ignored to prevent duplicate events from normal, non-moved events
                }
                else if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getComponent() != null && (e.getComponent().isFocusOwner() || e.getComponent().requestFocusInWindow())) {// if already focus owner process mouse event
                    super.processMouseEvent(e); 
                }
                else {
                    // Otherwise, just process as per normal.
                    if (e.getID() != MouseEvent.MOUSE_PRESSED) {
                        super.processMouseEvent(e); 
                    }
                }
            }
    }
    
    这种逻辑的核心是简单的问题。 按钮:你已经是焦点所有者了吗。 如果没有:您(按钮)是否可能获得焦点(请记住-shouldYieldFocus()是在requestFocusInWindow()调用中对当前焦点持有者调用的,如果无效,将始终返回false)

    这也有弹出错误对话框的副作用

    此逻辑阻止Java库ProcessMouseeEvent逻辑处理半个事件,而焦点系统阻止它完成


    显然,您需要在所有不同的JC组件上使用这种类型的逻辑来执行单击操作。

    请尝试将showMessageDialog调用包装成Runnable,并将其交给SwingUtilities::invokeLater(Runnable)@gd14 Hi,我尝试了您所述的方法,但似乎不起作用。修改后的代码如下:-final String message=“非法值:”+tf.getText();invokeLater(new Runnable(){public void run(){JOptionPane.showMessageDialog(null,消息,“非法值”,JOptionPane.ERROR_message);});返回false;我懂了。您的操作系统和Java版本是什么?我使用的是OSX和Java 1.6,它运行得很好。@gd1 OS-Windows7 64位和Java-1.6.27。我已经找到了一个解决方案,我正在详细说明b