TITLE:Cursorを砂時計に変更

Posted by at 2004-06-07

Cursorを砂時計に変更

処理中、マウスカーソルを砂時計に変更します。

  • &jnlp;
  • &jar;
  • &zip;
WaitCursor.png

サンプルコード

frame.setGlassPane(new LockingGlassPane());
frame.getGlassPane().setVisible(false);
button.addActionListener(new ActionListener() {
  @Override public void actionPerformed(ActionEvent e) {
    frame.getGlassPane().setVisible(true);
    button.setEnabled(false);
    new SwingWorker() {
      @Override public Object doInBackground() {
        dummyLongTask();
        return "Done";
      }
      @Override public void done() {
        frame.getGlassPane().setVisible(false);
        button.setEnabled(true);
      }
    }.execute();
  }
});
View in GitHub: Java, Kotlin
class LockingGlassPane extends JComponent {
  public LockingGlassPane() {
    setOpaque(false);
    setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
      @Override public boolean accept(Component c) {return false;}
    });
    addKeyListener(new KeyAdapter() {});
    addMouseListener(new MouseAdapter() {});
    requestFocusInWindow();
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  }
  @Override public void setVisible(boolean flag) {
    super.setVisible(flag);
    setFocusTraversalPolicyProvider(flag);
  }
}

解説

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

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

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


TABキーなどによるフォーカス移動を禁止する場合は、`GlassPane`に以下のような設定を行います。

  • どのコンポーネントにもフォーカス移動できない`FocusTraversalPolicy`を設定する
setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
  @Override public boolean accept(Component c) {return false;}
});
Set<AWTKeyStroke> s = Collections.emptySet();
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, s);
setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, s);

`Mnemonicなども禁止したい場合は、以下のようなGlassPane`を使用する方法があります(参考:Disabling Swing Containers, the final solution?)。

class LockingGlassPane extends JComponent {
  public LockingGlassPane() {
    setOpaque(false);
    super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  }
  @Override public void setVisible(boolean isVisible) {
    boolean oldVisible = isVisible();
    super.setVisible(isVisible);
    JRootPane rootPane = SwingUtilities.getRootPane(this);
    if(rootPane!=null && isVisible()!=oldVisible) {
      rootPane.getLayeredPane().setVisible(!isVisible);
    }
  }
  @Override public void paintComponent(Graphics g) {
    JRootPane rootPane = SwingUtilities.getRootPane(this);
    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 1.6.0`の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もあります。

この方法は、`JDK 1.5.0などの場合、WindowsLookAndFeel`で、Altキーを押すとメニューバーにフォーカスが移ることがあります。


`JDK 1.7.0の場合、JLayer`などを使用することで、特定のコンポーネントだけ入力不可にしてカーソルを砂時計にするといった設定が簡単に出来るようになっています。

参考リンク

コメント

  • TABキーで状態遷移しないようにするため、なにもしない`FocusTraversalPolicy`を追加しました。 -- aterai
  • 相当悩みました。`JDialog`だと同じことができないのは何でなんでしょうねぇ。。。 -- おれ
    • カーソルが変わらないのでしょうか? それともコンパイルエラーが出るとかでしょうか? -- aterai
  • 申し訳ない。カーソルが変わらないのだけれど、`1.5系でコンパイルするとだめみたい。同じソースでも1.4系でコンパイルするとちゃんと変わる。1.5`でのバグかな。。。 -- おれ
    • 追記。`JDialogのコンストラクタにnullを指定しているとこうなるようです。オーナフレームを指定してあげたら、1.5`でもきちんと出ました。お騒がせしました。 -- おれ
    • なるほど、`new JDialog((Frame)null);`で試してみるとカーソルが変わらないですね。情報どうもでした。 -- aterai
  • `DefaultFocusTraversalPolicy`を使うように変更しました。 -- aterai
  • `GlassPaneで、FocusTraversalPolicyを使わず、printを使ってMnemonic`などをブロックするように変更しました。 -- aterai
  • `SwingWorker`を使うように変更。 -- aterai