Java 鼠标点击被缓存?
我有一个带有登录屏幕的JavaSwing应用程序。登录屏幕上有一个提交按钮,用于在输入用户凭据后按下。按下按钮时,等待光标将使用其玻璃窗格投射到窗口上。还有一个默认的鼠标适配器,它对任何鼠标操作都不做任何操作Java 鼠标点击被缓存?,java,swing,jbutton,actionlistener,mouselistener,Java,Swing,Jbutton,Actionlistener,Mouselistener,我有一个带有登录屏幕的JavaSwing应用程序。登录屏幕上有一个提交按钮,用于在输入用户凭据后按下。按下按钮时,等待光标将使用其玻璃窗格投射到窗口上。还有一个默认的鼠标适配器,它对任何鼠标操作都不做任何操作 private final static MouseAdapter mouseAdapter = new MouseAdapter() {}; /** Sets cursor for specified component to Wait cursor */ public
private final static MouseAdapter mouseAdapter =
new MouseAdapter() {};
/** Sets cursor for specified component to Wait cursor */
public static void startWaitCursor(JComponent component) {
log.debug("startWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(WAIT_CURSOR);
glass.addMouseListener(mouseAdapter);
glass.setVisible(true);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
}
public static void stopWaitCursor(JComponent component) {
log.debug("stopWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(DEFAULT_CURSOR);
glass.removeMouseListener(mouseAdapter);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
glass.setVisible(false);
}
我假设这个设置在后端方法发生时保护我不受多次点击/按键的影响。我发现事实并非如此。因此,在ButtonListener.actionPerformed中,我添加了如下逻辑:
static boolean waiting = false;
class ButtonListener implements ActionListener {
ButtonListener() {
super();
}
public void actionPerformed(ActionEvent e) {
log.info("LoginWindow.ButtonListener.actionPerformed()");
LoginWindow.this.repaint(50);
if (!waiting) {
try {
waiting = true;
verifyLogin();
} finally {
waiting = false;
}
}
}
}
我发现这可以保护我不受按键的伤害,但不会被鼠标点击!如果在执行verifyLogin()时重复按submit按钮,则鼠标单击似乎被缓存在某个位置,在verifyLogin完成后,每次鼠标单击都会被处理
我对这里发生的事情感到非常困惑。有人有主意吗
更新:
嗯,通过遵循Cyrille Ka建议的方法:即在一个单独的线程中执行verifyLogin()方法并禁用按钮,我现在在多次单击鼠标后只得到两个事件,但第二个事件仍然令人烦恼
代码现在是:
public void actionPerformed(ActionEvent e) {
loginButton.setEnabled(false);
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
verifyLogin();
loginButton.setEnabled(true);
}});
}
但第二个事件仍然存在。我的日志显示,第二个事件发生在第一个事件发生后约280毫秒,但直到4秒后才执行,尽管setEnabled()是actionPerformed()事件所做的第一件事
2013-11-13 10:33:57186[AWT-EventQueue-0]信息
c、 a.r.s.c.g.LoginWindow-
LoginWindow.ButtonListener.actionPerformed()。事件发生在11月13日
2013年10:33:57.175 2013-11-13 10:34:01188[AWT-EventQueue-0]信息
c、 a.r.s.c.g.LoginWindow-
LoginWindow.ButtonListener.actionPerformed()。事件发生在11月13日
2013 10:33:57.453
我想我可以做一个黑客和丢弃事件的第二个旧的或什么,但这感觉丑陋。我一直在想,这应该没那么难
更新2:
来自JComponent.java的setEnabled()注释
*注意:禁用轻量级组件不会阻止它
*接收鼠标事件。
由于所有Swing组件都是轻量级的,并且setEnabled不会阻止组件接收鼠标事件,那么是什么阻止了这一点呢?我猜想
verifyLogin()
在登录完成之前一直处于阻塞状态。这样做只会阻塞Swing事件调度程序线程。当线程可用时,操作系统中的事件仍在排队等待发送到GUI
有两种方法可以防止用户重复单击:
- 只需禁用按钮:
并在流程完成时重新启用它button.setEnabled(false)
- 启动模式对话框(例如,使用等待动画),并在流程完成时将其删除
Thread
启动运行verifyLogin()
的任务,同时禁用按钮
我假设这个设置在后端方法发生时保护我不受多次点击/按键的影响。我发现事实并非如此
上的Swing教程部分提供了一个示例,说明了如何执行此操作。不记得它是否只处理MouseEvents或KeyEvents
在任何情况下,您也可以签出,它会处理这两个事件。此功能:
class ButtonListener implements ActionListener {
long previousEventEnd;
public void actionPerformed(ActionEvent e) {
if (e.getWhen() <= previousEventEnd ) {
log.tracef("discarding stale event, event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
return;
}
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
verifyLogin();
previousEventEnd = System.currentTimeMillis();
}
});
}
}
类按钮Listener实现ActionListener{
很久以前的事件结束;
已执行的公共无效操作(操作事件e){
如果(e.getWhen()您是否尝试过类似myButton.setEnabled(false)的操作
当它尝试登录时?是的,这也不起作用。我尝试了使用和不使用等待光标。在所有情况下,鼠标单击都是存储的。它应该可以工作。但是如果你不从GUI返回并在事件侦听器中执行工作,那么在事件侦听器返回之前可能没有注册禁用。@Cyrille_Ka“通过这样做”。这是什么“你指的是?@Cyrille_Ka-我想你指的是等待光标之类的东西。我想你可能是对的。但是,我仍然对为什么我的等待逻辑适用于按键而不适用于鼠标单击感到困惑。为什么操作系统不排队等待所有事件,而不仅仅是鼠标事件?我的意思是在事件侦听器中调用verifyLogin
。你说“如果在执行verifyLogin()时重复按提交按钮"因此,在未使用此事件的情况下,您正在发送单击事件。我认为按键的行为不应有所不同。可能焦点已移到应用程序中/外的其他内容?谢谢。我已经在使用玻璃窗格。我原以为它会阻止所有输入,但它没有。我仍在使用它,因为它与我的等待光标相关联,现在,使用基于AWTEvent.getWhen()的解决方案,我得到了我想要的控件。正如我在那里所说的,令人惊讶的是,没有简单的API来防止多次执行。我使用的API(getWhen())足够简单,但我找不到任何文档推荐使用它(或任何其他)目的。查看您的DisabledClassPane,我做了与您相同的鼠标单击操作。我只是添加了一个新的MouseAdapter()还有一个监听器。我可能很久以前就从您的示例中得到了这个想法。它不起作用,而且即使我实现了适配器来使用事件,它也不起作用。这是因为鼠标事件缓存在操作系统中的某个位置,一旦线程被解除阻止就会返回。唯一有效的方法似乎是在稍后我将其丢弃f他们的“时间”早于完成第一个事件启动的任务。@SteveCohen,对我来说很好(在风中)
class ButtonListener implements ActionListener {
long previousEventEnd;
public void actionPerformed(ActionEvent e) {
if (e.getWhen() <= previousEventEnd ) {
log.tracef("discarding stale event, event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
return;
}
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
verifyLogin();
previousEventEnd = System.currentTimeMillis();
}
});
}
}