0

ログイン画面のあるJavaスイングアプリケーションがあります。ログイン画面には、ユーザーの資格情報が入力された後に押すための送信ボタンがあります。ボタンが押されると、ガラス ペインを使用してウィンドウ上に待機カーソルが表示されます。また、マウス操作に対して何もしないデフォルトのマウス アダプタもあります。

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() メソッドを別のスレッドで実行し、ボタンを無効にすると、マウスを複数回クリックした後に 2 つのイベントしか取得できなくなりましたが、2 つ目のイベントは依然として煩わしいものです。

コードは次のとおりです。

      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);

            }});
      }

しかし、2 番目のイベントは引き続き発生します。私のログによると、2 番目のイベントは最初のイベントから約 280 ミリ秒後に発生しましたが、4 秒後まで実行されませんでした。 )イベントしました。

2013-11-13 10:33:57,186 [AWT-EventQueue-0] INFO carscgLoginWindow - LoginWindow.ButtonListener.actionPerformed()。2013 年 11 月 13 日 10:33:57.175 2013-11-13 10:34:01,188 [AWT-EventQueue-0] INFO carscgLoginWindow - LoginWindow.ButtonListener.actionPerformed() でイベントが発生しました。イベントは 2013 年 11 月 13 日 10:33:57.453 で発生しました

ハックして 1 秒以上前のイベントを破棄することもできると思いますが、それは見苦しく感じます。これはそれほど難しいことではないはずです、私は考え続けています。

更新 2: setEnabled() の JComponent.java からのコメント

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

すべての Swing コンポーネントは軽量であり、setEnabled はコンポーネントがマウス イベントを受信するのを妨げないので、何がこれを妨げますか?

4

3 に答える 3

1

バックエンドメソッドが実行されている間、このセットアップが複数回のクリック/キープレスから私を保護すると想定していました。そうではないことがわかりました。

The Glass Paneの 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 に答える