0

我有一个带有登录屏幕的 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] 信息 carcgLoginWindow - LoginWindow.ButtonListener.actionPerformed()。事件发生在 2013 年 11 月 13 日 10:33:57.175 2013-11-13 10:34:01,188 [AWT-EventQueue-0] 信息 carscgLoginWindow - LoginWindow.ButtonListener.actionPerformed()。事件发生在 2013 年 11 月 13 日 10:33:57.453

我想我可以做一个黑客攻击并丢弃第二个旧的事件或其他东西,但这感觉很难看。这应该不是那么难,我一直在想。

更新 2:来自 JComponent.java 对 setEnabled() 的评论

 * <p>Note: Disabling a lightweight component does not prevent it from
 * receiving MouseEvents.

由于所有的 Swing 组件都是轻量级的,并且 setEnabled 不会阻止组件接收鼠标事件,那么是什么阻止了这一点呢?

4

3 回答 3

1

我曾假设此设置可以在后端方法发生时保护我免受多次点击/按键。我发现事实并非如此。

Swing 教程中的部分玻璃窗格提供了一个示例,说明如何执行此操作。不记得它是否也只处理 MouseEvents 或 KeyEvents。

无论如何,您还可以查看Disabled Glass Pane,它确实处理这两个事件。

于 2013-11-13T16:56:31.920 回答
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 的诋毁者辩护。在这一点上,我没有任何辩护。这不应该是必要的。

于 2013-11-13T17:34:57.877 回答
0

我认为verifyLogin()在登录完成之前一直处于阻塞状态。通过这样做,您只是阻塞了 Swing 事件调度程序线程。当线程可用时,来自操作系统的事件仍在排队等待发送到您的 GUI。

有两种方法可以防止您的用户重复点击:

  • 只需禁用按钮:button.setEnabled(false);并在过程完成后重新启用它。
  • 启动一个模态对话框(例如带有等待动画)并在进程完成时将其删除。

编辑:通常,您应该从事件侦听器中快速返回,因为您不想阻止所有 GUI,只阻止某些部分,并且无论如何它都会让您的应用程序感觉迟缓(如果在此期间窗口不会重新绘制它被移动或其他东西)。用于Thread启动运行 a 的任务verifyLogin()并同时禁用您的按钮。

于 2013-11-13T15:31:45.907 回答