6

これを標準的な方法で行うことは可能ですか?

これがシナリオです。

  1. EDT でコストのかかる作業を開始します (コストのかかる操作が終了するまで、EDT はブロックされます)。

  2. EDT がブロックされている間、ユーザーはマウス ボタンをクリック/ドラッグし続けました。すべてのマウス操作はどこかに記録されます。

  3. EDT が解放されると (高価なものがなくなると)、マウス イベントの処理が開始されます。

ステップ 3 で必要なのは、積み重なったマウス イベントを破棄することです。EDT が解放された後、新しいマウス イベントは通常の方法で処理する必要があります。

これを達成する方法についてのアイデア。

PS: EDT がブロックされるのを防ぐことはできません (プログラム内の一部のモジュールの動作を制御していません)。

編集:「SunToolkit.flushPendingEvents()」を安全に呼び出すことができれば、EDT で高価な操作を開始する前にいつでもガラス板を配置できます。高価な操作が終了した後、EDT スレッドですべてのイベントをフラッシュします。イベントは、何もしないグラス ペインに移動します。その後、EDT を通常どおりに機能させます。

EDIT2: 問題を示すために SSCCE を追加しました。

public class BusyCursorTest2 extends javax.swing.JFrame {

    public BusyCursorTest2() {

        javax.swing.JButton wait = new javax.swing.JButton("3 秒待ちます");
        getContentPane().setLayout(new java.awt.GridLayout(2, 1, 0, 0));
        getContentPane().add(待機);
        getContentPane().add(new javax.swing.JToggleButton("Click me"));
        setTitle("ビジー カーソル");
        setSize(300, 200);
        setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
        setVisible(真);

        wait.addActionListener(新しい java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent イベント) {

                final java.util.Timer timer = switchToBusyCursor(BusyCursorTest2.this);

                試す {
                    // EDT で高価なことをする
                    試す {
                        Thread.sleep(3000);
                    キャッチ(InterruptedException e){
                        //何もしない
                    }
                } 最後に {
                    switchToNormalCursor(BusyCursorTest2.this, timer);
                }
            }

        });
    }

    public static java.util.Timer switchToBusyCursor(最終の javax.swing.JFrame フレーム) {
        startEventTrap(フレーム);
        java.util.TimerTask timerTask = 新しい java.util.TimerTask() {

            public void run() {
                startWaitCursor(フレーム);
            }

        };
        final java.util.Timer timer = new java.util.Timer();
        timer.schedule(timerTask, DELAY_MS);
        タイマーを返します。
    }

    public static void switchToNormalCursor(最終的な javax.swing.JFrame フレーム、最終的な java.util.Timer タイマー) {
        timer.cancel();
        stopWaitCursor(フレーム);
        stopEventTrap(フレーム);
    }

    private static void startWaitCursor(javax.swing.JFrame フレーム) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopWaitCursor(javax.swing.JFrame フレーム) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static void startEventTrap(javax.swing.JFrame フレーム) {
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopEventTrap(javax.swing.JFrame フレーム) {
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                新しい BusyCursorTest2();
            }

        });

    }

    プライベート静的最終 int DELAY_MS = 250;

}
  1. SSCCE を実行する

  2. 「3 秒待つ」ボタンをクリックします。高価な操作をシミュレートします。マウス カーソルがビジーに変わります。

  3. カーソルがビジー状態のときに、トグル ボタン [Click me] をクリックします。3 秒後にトグル ボタンの状態が変化した場合、マウス イベントはトグル ボタンによって受信され、トラップされませんでした。

カーソルがビジーに見える間、生成されたマウス (およびその他の) イベントが破棄されるようにします。

ありがとう。

4

3 に答える 3

5

OK、ようやくすべてが機能するようになりました。正しく動作する例として SSCCE を投稿しています。トリックは、「javax.swing.SwingUtilities.invokeLater()」メソッドを使用してガラスペインを非表示にすることです。必要なコードを Runnable でラップし、invokeLater を使用して呼び出します。このような場合、Swing はすべてのマウス イベントを処理し (グラスペインがそれらをインターセプトするため何も起こりません)、グラスペインを非表示にします。こちらがSSCCEです。

public class BusyCursorTest2 extends javax.swing.JFrame {

    public BusyCursorTest2() {

        javax.swing.JButton wait = new javax.swing.JButton("3 秒待ちます");
        getContentPane().setLayout(new java.awt.GridLayout(2, 1, 0, 0));
        getContentPane().add(待機);
        getContentPane().add(new javax.swing.JToggleButton("Click me"));
        setTitle("ビジー カーソル");
        setSize(300, 200);
        setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
        setVisible(真);

        wait.addActionListener(新しい java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent イベント) {

                final java.util.Timer timer = switchToBusyCursor(BusyCursorTest2.this);

                試す {
                    // EDT などで高価な処理を行う
                    試す {
                        Thread.sleep(3000);
                    キャッチ(InterruptedException e){
                        //何もしない
                    }
                } 最後に {
                    switchToNormalCursorEventThread(BusyCursorTest2.this, timer);
                }

            }

        });
    }

    public static java.util.Timer switchToBusyCursor(最終の javax.swing.JFrame フレーム) {
        startEventTrap(フレーム);
        java.util.TimerTask timerTask = 新しい java.util.TimerTask() {

            public void run() {
                startWaitCursor(フレーム);
            }

        };
        final java.util.Timer timer = new java.util.Timer();
        timer.schedule(timerTask, DELAY_MS);
        タイマーを返します。
    }

    public static void switchToNormalCursorEventThread(最終的な javax.swing.JFrame フレーム、最終的な java.util.Timer タイマー) {

        ランナブル r = new Runnable() {

            public void run() {
                switchToNormalCursor(フレーム、タイマー);
            }

        };

        javax.swing.SwingUtilities.invokeLater(r);

    }

    public static void switchToNormalCursor(最終的な javax.swing.JFrame フレーム、最終的な java.util.Timer タイマー) {
        timer.cancel();
        stopWaitCursor(フレーム);
        stopEventTrap(フレーム);
    }

    private static void startWaitCursor(javax.swing.JFrame フレーム) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopWaitCursor(javax.swing.JFrame フレーム) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static void startEventTrap(javax.swing.JFrame フレーム) {
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopEventTrap(javax.swing.JFrame フレーム) {
        java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                新しい BusyCursorTest2();
            }

        });

    }

    プライベート静的最終 int DELAY_MS = 250;

}

繰り返しますが、可能な限り EDT をブロックしてはなりません。ただし、必要に応じて、上記のようにビジー カーソルを動作させることができます。

どんなコメントでも大歓迎です。

于 2011-08-23T21:53:32.007 に答える
2

この記事を読んでください。

基本的に、長時間実行されるタスクは EDT で実行しないでください。Java は、そのようなタスクのために SwingWorker を提供しています。

もっと詳しく説明しますが、あなたは答えを受け入れる傾向がありません。

于 2011-08-17T15:47:57.250 に答える
1

絶対に EDT をブロックしないでください。そんなことは絶対にしてはいけない!

これを行うための簡単なユーティリティ クラスを次に示します (Santosh Tiwari の功績):

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * When blocking the EDT (Event Queue) in swing, the cursor won't update, and windows won't render.
 * This should show the hourglass even when you're blocking the EDT.
 *
 * Source:
 * https://stackoverflow.com/questions/7085239/java-swing-clear-the-event-queue
 * 
 * @author Kieveli, Santosh Tiwari
 *
 */
public class BlockingWaitCursor {

   private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {};

   /**
    * The Dialog or main window is required to show the cursor and animate it. The actionListener is called
    * as soon as initial setup is completed and the animation timer is running.
    * @param currentComponent A panel, dialog, frame, or any other swing component that is the current focus
    * @param action Your action to perform on the EDT. This is started extremely quickly and without delay.
    */
   public static void showWaitAndRun(Component currentComponent, ActionListener action ) {

      Timer timer = setupWaitCursor(currentComponent);

      try {
         // now allow our caller to execute their slow and delayed code on the EDT
         ActionEvent event = new ActionEvent(BlockingWaitCursor.class, ActionEvent.ACTION_PERFORMED, "run");
         action.actionPerformed(event);
      }
      finally {
         resetWaitCursor(currentComponent, timer);
      }
   }

   private static Timer setupWaitCursor(Component currentComponent) {
      final Component glassPane = findGlassPane(currentComponent);
      if ( glassPane == null ) {
         return null;
      }

      // block mouse-actions with a glass pane that covers everything
      glassPane.addMouseListener(mouseAdapter);
      glassPane.setVisible(true);

      // animate the wait cursor off of the EDT using a generic timer.
      Timer timer = new Timer();
      timer.schedule( new TimerTask() {
         @Override
         public void run() {
            glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            glassPane.addMouseListener(mouseAdapter);
            glassPane.setVisible(true);
         }

      }, 250l);

      return timer;
   }

   private static void resetWaitCursor(Component currentComponent, final Timer timer) {
      final Component glassPane = findGlassPane(currentComponent);
      if ( glassPane == null ) {
         return;
      }
      // Invoke later so that the event queue contains user actions to cancel while the loading occurred
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            if ( timer != null )
               timer.cancel();
            Toolkit.getDefaultToolkit().getSystemEventQueue();
            glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            glassPane.removeMouseListener(mouseAdapter);
            glassPane.setVisible(false);  
         }
      });
   }

   private static Component findGlassPane(Component currentComponent) {
      // try to locate the glass pane by looking for a frame or dialog as an ancestor
      JFrame frame = findFrame(currentComponent);
      JDialog dialog = findDialog(currentComponent);
      Component glassPane = null;
      if ( frame != null )
         glassPane = frame.getGlassPane();
      if ( dialog != null )
         glassPane = dialog.getGlassPane();
      return glassPane;
   }

   private static JFrame findFrame(Component currentComponent) {
      // find the frame if it exists - it may be the currentComponent
      if ( currentComponent instanceof JFrame )
         return (JFrame) currentComponent;

      Window window = SwingUtilities.getWindowAncestor(currentComponent);
      if ( window == null )
         return null;
      if ( ! (window instanceof JFrame) )
         return null;
      return (JFrame)window;
   }

   private static JDialog findDialog(Component currentComponent) {
      // find the dialog if it exists - it may be the currentComponent
      if ( currentComponent instanceof JDialog )
         return (JDialog) currentComponent;

      Window window = SwingUtilities.getWindowAncestor(currentComponent);
      if ( window == null )
         return null;
      if ( ! (window instanceof JDialog) )
         return null;
      return (JDialog)window;
   }

}

しかし、決して使用しないでください。まあ、誇りに思っておらず、すぐに手に負えなくなってメインアプリケーションになった簡単なユーティリティを書いていて、ワーカーで何が実行でき、何が実行されるかを理解するためにコードを分解する時間がない場合を除きます。スレッドセーフではない swing / sql との統合が原因で壊れます。

于 2018-06-28T14:42:32.720 に答える