鼠标点击被缓存?

时间:2013-11-13 15:27:00

标签: java swing jbutton actionlistener mouselistener

我有一个带有登录屏幕的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不会阻止组件接收鼠标事件,这有什么作用呢?

3 个答案:

答案 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给它的批评者。在这里,我没有防守。这不是必要的。