我有一个带有登录屏幕的java swing应用程序。登录屏幕有一个提交按钮,用于在输入用户凭据后按下。按下按钮时,使用其玻璃窗格将等待光标向上抛出窗口。还有一个默认的鼠标适配器,它不会对任何鼠标操作执行任何操作。
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()时反复按下提交按钮,则鼠标点击似乎在某处缓存,验证登录完成后,每次点击鼠标都会被处理!
我对这里发生的事情感到非常困惑。有人有想法吗?
更新
嗯,遵循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:57,186 [AWT-EventQueue-0] INFO c.a.r.s.c.g.LoginWindow - LoginWindow.ButtonListener.actionPerformed()。事件发生在11月13日 2013 10:33:57.175 2013-11-13 10:34:01,188 [AWT-EventQueue-0] INFO c.a.r.s.c.g.LoginWindow - LoginWindow.ButtonListener.actionPerformed()。事件发生在11月13日 2013 10:33:57.453
我想我可以做一个黑客并丢弃一秒钟以上的事件,但这感觉很难看。这不应该那么困难,我一直在想。
更新2: 来自JComponent.java的setEnabled()
的注释* <p>Note: Disabling a lightweight component does not prevent it from * receiving MouseEvents.
由于所有Swing组件都是轻量级的,并且setEnabled不会阻止组件接收鼠标事件,这有什么作用呢?
答案 0 :(得分:1)
我曾假设这种设置可以保护我免受多次点击/按键的影响,而后端方法正在进行中。我发现事实并非如此。
The Glass Pane上的Swing教程中的部分提供了一个如何执行此操作的示例。不记得它是否只处理MouseEvents或KeyEvents。
在任何情况下,您也可以查看Disabled Glass Pane,它会处理这两个事件。
答案 1 :(得分:0)
我认为verifyLogin()
阻止,直到登录完成。通过这样做,您只是阻止Swing事件调度程序线程。当线程可用时,来自操作系统的事件仍然排队等待发送到您的GUI。
有两种方法可以阻止用户重复点击:
button.setEnabled(false);
并在流程完成后将其启用。 编辑:一般情况下,您应该从事件监听器快速返回,因为您不想阻止所有GUI,只有某些部分,并且无论如何它会让您的应用感到迟钝(如果它被移动或其他东西,窗口将不会在此期间重新绘制)。使用Thread
启动运行verifyLogin()
的任务,并在此期间停用您的按钮。
答案 2 :(得分:0)
这有效:
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();
}
});
}
}
我不得不承认我很惊讶。我通常会诋毁Java给它的批评者。在这里,我没有防守。这不是必要的。