Java 当焦距被JOptionPane窃取时,JButton保持按下状态
我刚开始荡秋千,我有一种情况。我正在设计一个基于xml文件输入(元数据)动态呈现GUI组件的应用程序。现在,出于验证目的,我的大多数JTextField都设置了InputVerifier。每当有无效输入时,输入验证器就会弹出JOptionPane 现在,如果用户输入一个无效数据并继续前进并单击面板上的按钮,则会弹出一个对话框,用户必须对此做出响应。但在此之后,按钮也不会绘制到释放状态。它看起来还是被压着的,但实际上不是。由于整个代码相当混乱,我将问题场景放在下面的代码中:- 我应该怎么做才能使JButton看起来没有压力?如果能解释一下逻辑,我将不胜感激 提前谢谢Java 当焦距被JOptionPane窃取时,JButton保持按下状态,java,swing,jtextfield,joptionpane,inputverifier,Java,Swing,Jtextfield,Joptionpane,Inputverifier,我刚开始荡秋千,我有一种情况。我正在设计一个基于xml文件输入(元数据)动态呈现GUI组件的应用程序。现在,出于验证目的,我的大多数JTextField都设置了InputVerifier。每当有无效输入时,输入验证器就会弹出JOptionPane 现在,如果用户输入一个无效数据并继续前进并单击面板上的按钮,则会弹出一个对话框,用户必须对此做出响应。但在此之后,按钮也不会绘制到释放状态。它看起来还是被压着的,但实际上不是。由于整个代码相当混乱,我将问题场景放在下面的代码中:- 我应该怎么做才能使J
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的好地方
有几种方法可以解决你的问题:
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安装的侦听器:
- 找到原件
- 实现一个自定义侦听器,将大多数事件直接委托给原始侦听器
- 对于按下的事件,首先请求焦点:如果已将委托给原始,则不执行任何操作
- 查找根窗格的已按下操作
- 实现一个自定义操作,检查是否有可能被否决的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