• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:Cursorを砂時計に変更
#navi(../)
RIGHT:Posted by [[aterai]] at 2004-06-07
*Cursorを砂時計に変更 [#w760af9f]
処理中、マウスカーソルを砂時計に変更します。
---
category: swing
folder: WaitCursor
title: Cursorを砂時計に変更
tags: [Cursor, GlassPane, FocusTraversalPolicy, SwingWorker]
author: aterai
pubdate: 2004-06-07T13:57:16+09:00
description: バックグラウンドで処理が実行されている間は、Cursorに砂時計が設定されたGlassPaneを有効にします。
image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTWfYWDbsI/AAAAAAAAApU/rldJwQuVm-8/s800/WaitCursor.png
---
* 概要 [#summary]
バックグラウンドで処理が実行されている間は、`Cursor`に砂時計が設定された`GlassPane`を有効にします。

-&jnlp;
-&jar;
-&zip;
#download(https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTWfYWDbsI/AAAAAAAAApU/rldJwQuVm-8/s800/WaitCursor.png)

//#screenshot
#ref(http://lh5.ggpht.com/_9Z4BYR88imo/TQTWfYWDbsI/AAAAAAAAApU/rldJwQuVm-8/s800/WaitCursor.png)

**サンプルコード [#r111147b]
#code{{
* サンプルコード [#sourcecode]
#code(link){{
frame.setGlassPane(new LockingGlassPane());
frame.getGlassPane().setVisible(false);
button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    //System.out.println("actionPerformed: " + EventQueue.isDispatchThread());
  @Override public void actionPerformed(ActionEvent e) {
    frame.getGlassPane().setVisible(true);
    button.setEnabled(false);
    Thread t = new Thread() {
      public void run() {
        //System.out.println("Thread: " + EventQueue.isDispatchThread());
        dummyLongTask();
        EventQueue.invokeLater(new Runnable() {
          public void run() {
            //System.out.println("invokeLater: " + EventQueue.isDispatchThread());
            frame.getGlassPane().setVisible(false);
            button.setEnabled(true);
          }
        });
    new SwingWorker() {
      @Override public Object doInBackground() {
        doSomeLongTask();
        return "Done";
      }
    };
    //t.setPriority(Thread.MIN_PRIORITY);
    t.start();

      @Override public void done() {
        frame.getGlassPane().setVisible(false);
        button.setEnabled(true);
      }
    }.execute();
  }
});
}}
#code{{
// ...
class LockingGlassPane extends JComponent {
  public LockingGlassPane() {
    setOpaque(false);
    setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
      public boolean accept(Component c) {return false;}
      @Override public boolean accept(Component c) {
        return false;
      }
    });
    addKeyListener(new KeyAdapter() {});
    addMouseListener(new MouseAdapter() {});
    requestFocusInWindow();
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  }
  public void setVisible(boolean flag) {

  @Override public void setVisible(boolean flag) {
    super.setVisible(flag);
    setFocusTraversalPolicyProvider(flag);
  }
}
}}

**解説 [#a497267c]
上記のサンプルでは、カーソルを砂時計に変更し、なにもしないマウスリスナーなどを設定したGlassPaneをJFrame#setGlassPane()メソッドでフレームに追加しています。
* 解説 [#explanation]
上記のサンプルでは、カーソルを砂時計に変更し、なにもしないマウスリスナーなどを設定した`GlassPane`を`JFrame#setGlassPane()`メソッドでフレームに追加しています。

スタートボタンがクリックされて処理が継続している間は、このGlassPaneが有効になり、マウス、キー、フォーカス移動などのイベントが、すべてGlassPaneに奪われるため、フレーム内のコンポーネントをアクセス不可にすることが出来ます。
スタートボタンがクリックされて処理が継続している間はこの`GlassPane`が有効になり、マウス、キー入力、フォーカス移動などのイベントがすべて`GlassPane`に奪われるため、フレーム内のコンポーネントがアクセス不可状態になります。

このため、サンプルにあるsetEnabled(true)なJTextFieldの上にマウスポインタを移動しても、処理中はカーソルアイコンは砂時計のまま変化しません。
このため、サンプルにある`setEnabled(true)`な`JTextField`の上にマウスポインタを移動しても処理中はカーソルアイコンは砂時計のまま変化しません。

----
タブキーなどによるフォーカス移動を禁止する場合は、GlassPaneに以下のような設定を行います。
-どのコンポーネントにもフォーカス移動できないFocusTraversalPolicyを設定する
KBD{Tab}キーなどによるフォーカス移動を禁止する場合は、`GlassPane`に以下のような設定を行います。

- どのコンポーネントにもフォーカス移動できない`FocusTraversalPolicy`を設定する

#code{{
setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
  public boolean accept(Component c) {return false;}
  @Override public boolean accept(Component c) {
    return false;
  }
});
}}

-または、TraversalKeysを空にする
--参考:[http://forums.sun.com/thread.jspa?threadID=791415 Swing - How to display "Loading data..." to the user]
- または、`TraversalKeys`を空にする
-- 参考: [https://community.oracle.com/thread/1375257 Swing - How to display "Loading data..." to the user]

#code{{
Set<AWTKeyStroke> s = Collections.emptySet();
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, s);
setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, s);
}}

----
Mnemonic なども禁止したい場合は、以下のようなGlassPaneを使用する方法があります(参考:[http://weblogs.java.net/blog/alexfromsun/archive/2008/01/ Disabling Swing Containers, the final solution?])。
`Mnemonic`なども禁止したい場合は、以下のような`GlassPane`を使用する方法があります(参考: [http://weblogs.java.net/blog/alexfromsun/archive/2008/01/ Disabling Swing Containers, the final solution?])。

#code{{
class LockingGlassPane extends JComponent {
  public LockingGlassPane() {
    setOpaque(false);
    super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  }
  @Override
  public void setVisible(boolean isVisible) {

  @Override public void setVisible(boolean isVisible) {
    boolean oldVisible = isVisible();
    super.setVisible(isVisible);
    JRootPane rootPane = SwingUtilities.getRootPane(this);
    if(rootPane!=null && isVisible()!=oldVisible) {
    if (rootPane != null && isVisible() != oldVisible) {
      rootPane.getLayeredPane().setVisible(!isVisible);
    }
  }
  @Override
  public void paintComponent(Graphics g) {

  @Override protected void paintComponent(Graphics g) {
    JRootPane rootPane = SwingUtilities.getRootPane(this);
    if(rootPane!=null) {
    if (rootPane != null) {
      // http://weblogs.java.net/blog/alexfromsun/archive/2008/01/
      // it is important to call print() instead of paint() here
      // because print() doesn't affect the frame's double buffer
      rootPane.getLayeredPane().print(g);
    }
    super.paintComponent(g);
  }
}
}}

----
JDK 6 の場合、[http://tips4java.wordpress.com/2008/11/07/disabled-glass-pane/ Disabled Glass Pane « Java Tips Weblog]のようにキー入力を無効にするキーリスナーを追加する方法もあります。
- `JDK 1.6.0`の場合、[https://tips4java.wordpress.com/2008/11/07/disabled-glass-pane/ Disabled Glass Pane « Java Tips Weblog]のようにキー入力を無効にするキーリスナーを追加する方法もある
-- この方法は、`JDK 1.5.0`などの場合、`WindowsLookAndFeel`で、KBD{Alt}キーを押すとメニューバーにフォーカスが移動する場合がある

この方法は、JDK 5 などの場合、WindowsLnF で、Altキーを押すとメニューバーにフォーカスが移ることがあります。
----
- `JDK 1.7.0`の場合、`JLayer`を使用して特定のコンポーネントだけ入力不可、かつカーソルを砂時計に変更するなどの設定が可能になった
-- [[JLayerで指定したコンポーネントへの入力を禁止>Swing/DisableInputLayer]] に移動

**参考リンク [#xaea8879]
-[http://forums.sun.com/thread.jspa?threadID=791415 Swing - How to display "Loading data..." to the user]
-[http://weblogs.java.net/blog/alexfromsun/archive/2008/01/ Disabling Swing Containers, the final solution?]
--[[JInternalFrameをModalにする>Swing/ModalInternalFrame]]
-[http://tips4java.wordpress.com/2008/11/07/disabled-glass-pane/ Disabled Glass Pane « Java Tips Weblog]
* 参考リンク [#reference]
- [https://community.oracle.com/thread/1375257 Swing - How to display "Loading data..." to the user]
- [http://weblogs.java.net/blog/alexfromsun/archive/2008/01/ Disabling Swing Containers, the final solution?]
-- [[JInternalFrameをModalにする>Swing/ModalInternalFrame]]
- [https://tips4java.wordpress.com/2008/11/07/disabled-glass-pane/ Disabled Glass Pane « Java Tips Weblog]

**コメント [#h83774c4]
-Tabキーで状態遷移しないようにするため、なにもしないFocusTraversalPolicyを追加しました。 -- [[aterai]] &new{2005-04-18 (月) 10:51:25};
- 相当悩みました。JDialog だと同じことができないのは何でなんでしょうねぇ。。。 -- [[おれ]] &new{2006-05-17 (水) 16:33:12};
-- カーソルが変わらないのでしょうか? それともコンパイルエラーが出るとかでしょうか? -- [[aterai]] &new{2006-05-17 (水) 17:59:14};
- 申し訳ない。カーソルが変わらないのだけれど、1.5系でコンパイルするとだめみたい。同じソースでも1.4系でコンパイルするとちゃんと変わる。1.5でのバグかな。。。 -- [[おれ]] &new{2006-05-18 (木) 12:58:11};
-- 追記。JDialog のコンストラクタに null を指定しているとこうなるようです。オーナフレームを指定してあげたら、1.5でもきちんと出ました。お騒がせしました。 -- [[おれ]]
-- なるほど、new JDialog((Frame)null);で試してみるとカーソルが変わらないですね。情報どうもでした。 -- [[aterai]] &new{2006-05-18 (木) 21:45:15};
- DefaultFocusTraversalPolicy を使うように変更しました。 -- [[aterai]] &new{2007-07-03 (火) 16:39:12};
- GlassPaneで、FocusTraversalPolicyを使わず、print を使ってMnemonicなどをブロックするように変更しました。 -- [[aterai]] &new{2008-04-15 (火) 17:14:09};
* コメント [#comment]
#comment
- KBD{Tab}キーで状態遷移しないようにするため、なにもしない`FocusTraversalPolicy`を追加しました。 -- &user(aterai); &new{2005-04-18 (月) 10:51:25};
- 相当悩みました。`JDialog`だと同じことができないのは何でなんでしょうねぇ。。。 -- &user(おれ); &new{2006-05-17 (水) 16:33:12};
-- カーソルが変わらないのでしょうか? それともコンパイルエラーが出るとかでしょうか? -- &user(aterai); &new{2006-05-17 (水) 17:59:14};
- 申し訳ない。カーソルが変わらないのだけれど、`1.5`系でコンパイルするとだめみたい。同じソースでも`1.4`系でコンパイルするとちゃんと変わる。`1.5`でのバグかな。。。 -- &user(おれ); &new{2006-05-18 (木) 12:58:11};
-- 追記。`JDialog`のコンストラクタに`null`を指定しているとこうなるようです。オーナフレームを指定してあげたら、`1.5`でもきちんと出ました。お騒がせしました。 -- &user(おれ);
-- なるほど、`new JDialog((Frame)null);`で試してみるとカーソルが変わらないですね。情報どうもでした。 -- &user(aterai); &new{2006-05-18 (木) 21:45:15};
- `DefaultFocusTraversalPolicy`を使うように変更しました。 -- &user(aterai); &new{2007-07-03 (火) 16:39:12};
- `GlassPane`で、`FocusTraversalPolicy`を使わず、`print`を使って`Mnemonic`などをブロックするように変更しました。 -- &user(aterai); &new{2008-04-15 (火) 17:14:09};
- `SwingWorker`を使うように変更。 -- &user(aterai); &new{2011-03-26 (土) 23:21:11};
- 入力抑制であれば`AWTEventListener`を追加して`InputEvent`を`consume`しちゃえば良いのでは・・・? -- &user(sawshun); &new{2014-01-14 (火) 08:32:09};
-- `AWTEventListener`を使うのは便利な方法だと思いますが、セキュリティマネージャの設定によっては`SecurityException`が発生したり、ドキュメントには「[https://docs.oracle.com/javase/jp/8/docs/api/java/awt/Toolkit.html#addAWTEventListener-java.awt.event.AWTEventListener-long- アクセシビリティー、イベントの記録と再生、および診断トレースなどの特別な機能をサポートすることを主な目的としているので、アプリケーションの使用では推奨されません。]」的な注意事項があるので、使い所を考慮する必要がありそうです。このサンプルのような場合で`InputEvent#consume()`を使うなら`1.7`以上で`JLayer`を使用する方が無難かもしれません。 -- &user(aterai); &new{2014-01-14 (火) 14:13:39};

#comment