Swing/WaitCursor のバックアップの現在との差分(No.12)
TITLE:Cursorを砂時計に変更
Posted by aterai at 2004-06-07
Cursorを砂時計に変更
処理中、マウスカーソルを砂時計に変更します。- 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:
概要
バックグラウンドで処理が実行されている間は、Cursor
に砂時計が設定されたGlassPane
を有効にします。
- &jnlp;
- &jar;
- &zip;
Screenshot
Advertisement
サンプルコード
#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
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();
doSomeLongTask();
return "Done";
}
#spanadd
#spanend
@Override public void done() {
frame.getGlassPane().setVisible(false);
button.setEnabled(true);
}
}.execute();
#spandel
}
#spanend
}.execute();
}
});
#spandel
#spanend
#spanadd
// ...
#spanend
class LockingGlassPane extends JComponent {
public LockingGlassPane() {
setOpaque(false);
setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
@Override 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));
}
#spanadd
#spanend
@Override public void setVisible(boolean flag) {
super.setVisible(flag);
setFocusTraversalPolicyProvider(flag);
}
}
解説
上記のサンプルでは、カーソルを砂時計に変更し、なにもしないマウスリスナーなどを設定したGlassPaneをJFrame#setGlassPane()メソッドでフレームに追加しています。解説
上記のサンプルでは、カーソルを砂時計に変更し、なにもしないマウスリスナーなどを設定したGlassPane
をJFrame#setGlassPane()
メソッドでフレームに追加しています。
スタートボタンがクリックされて処理が継続している間は、このGlassPaneが有効になり、マウス、キー、フォーカス移動などのイベントが、すべてGlassPaneに奪われるため、フレーム内のコンポーネントをアクセス不可にすることが出来ます。
スタートボタンがクリックされて処理が継続している間はこのGlassPane
が有効になり、マウス、キー入力、フォーカス移動などのイベントがすべてGlassPane
に奪われるため、フレーム内のコンポーネントがアクセス不可状態になります。
このため、サンプルにあるsetEnabled(true)なJTextFieldの上にマウスポインタを移動しても、処理中はカーソルアイコンは砂時計のまま変化しません。
このため、サンプルにあるsetEnabled(true)
なJTextField
の上にマウスポインタを移動しても処理中はカーソルアイコンは砂時計のまま変化しません。
タブキーなどによるフォーカス移動を禁止する場合は、GlassPaneに以下のような設定を行います。
- どのコンポーネントにもフォーカス移動できないFocusTraversalPolicyを設定する
Tabキーなどによるフォーカス移動を禁止する場合は、
GlassPane
に以下のような設定を行います。
- どのコンポーネントにもフォーカス移動できない
FocusTraversalPolicy
を設定する
setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
@Override public boolean accept(Component c) {return false;}
@Override public boolean accept(Component c) {
return false;
}
});
- または、TraversalKeysを空にする
- または、
TraversalKeys
を空にする
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?)。
Mnemonic
なども禁止したい場合は、以下のようなGlassPane
を使用する方法があります(参考: Disabling Swing Containers, the final solution?)。
class LockingGlassPane extends JComponent {
public LockingGlassPane() {
setOpaque(false);
super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
#spanadd
#spanend
@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) {
#spanadd
#spanend
@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 の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もあります。
-
JDK 1.6.0
の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もある- この方法は、
JDK 1.5.0
などの場合、WindowsLookAndFeel
で、Altキーを押すとメニューバーにフォーカスが移動する場合がある
- この方法は、
JDK 7 の場合、JLayerなどを使用することで、特定のコンポーネントだけ入力不可にしてカーソルを砂時計にするといった設定が簡単に出来るようになっています。
#spanend
#spandel
import java.awt.*;
#spanend
#spandel
import java.awt.event.*;
#spanend
#spandel
import java.beans.PropertyChangeEvent;
#spanend
#spandel
import javax.swing.*;
#spanend
#spandel
import javax.swing.plaf.LayerUI;
#spanend
#spandel
public class DisableInputLayerUITest {
#spanend
public JComponent makeUI() {
final JPanel p = new JPanel();
final DisableInputLayerUI layerUI = new DisableInputLayerUI();
final JLayer<JPanel> jlayer = new JLayer<JPanel>(p, layerUI);
final Timer stopper = new Timer(4000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.stop();
}
});
p.add(new JCheckBox());
p.add(new JTextField(10));
p.add(new JButton(new AbstractAction("button") {
public void actionPerformed(ActionEvent e) {
layerUI.start();
if (!stopper.isRunning()) {
stopper.start();
}
}
}));
stopper.setRepeats(false);
return jlayer;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() { createAndShowGUI(); }
});
}
public static void createAndShowGUI() {
JComponent c = new DisableInputLayerUITest().makeUI();
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(c, BorderLayout.NORTH);
f.getContentPane().add(new JScrollPane(new JTextArea()));
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#spandel
}
#spanend
- `JDK 1.7.0`の場合、`JLayer`を使用して特定のコンポーネントだけ入力不可、かつカーソルを砂時計に変更するなどの設定が可能になった
-- [[JLayerで指定したコンポーネントへの入力を禁止>Swing/DisableInputLayer]] に移動
#spandel
class DisableInputLayerUI extends LayerUI<JPanel> {
#spanend
private boolean isRunning = false;
@Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if(!isRunning) return;
Graphics2D g2 = (Graphics2D) g.create();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f));
g2.setPaint(Color.GRAY);
g2.fillRect(0, 0, c.getWidth(), c.getHeight());
g2.dispose();
}
@Override public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK);
}
@Override public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
@Override public void eventDispatched(AWTEvent e, JLayer l) {
if(isRunning && e instanceof InputEvent) {
((InputEvent)e).consume();
}
}
private static final String CMD_REPAINT = "repaint";
public void start() {
if (isRunning) return;
isRunning = true;
firePropertyChange(CMD_REPAINT,false,true);
}
public void stop() {
isRunning = false;
firePropertyChange(CMD_REPAINT,true,false);
}
@Override public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
String cmd = pce.getPropertyName();
if(CMD_REPAINT.equals(cmd)) {
l.getGlassPane().setVisible((Boolean)pce.getNewValue());
l.repaint();
}
}
#spandel
}
#spanend
#spandel
参考リンク
- Swing - How to display "Loading data..." to the user
- Disabling Swing Containers, the final solution?
- Disabled Glass Pane « Java Tips Weblog
コメント
- 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 - 入力抑制であれば
AWTEventListener
を追加してInputEvent
をconsume
しちゃえば良いのでは・・・? -- sawshun-
AWTEventListener
を使うのは便利な方法だと思いますが、セキュリティマネージャの設定によってはSecurityException
が発生したり、ドキュメントには「アクセシビリティー、イベントの記録と再生、および診断トレースなどの特別な機能をサポートすることを主な目的としているので、アプリケーションの使用では推奨されません。」的な注意事項があるので、使い所を考慮する必要がありそうです。このサンプルのような場合でInputEvent#consume()
を使うなら1.7
以上でJLayer
を使用する方が無難かもしれません。 -- aterai
-
参考リンク
- Swing - How to display "Loading data..." to the user
- Disabling Swing Containers, the final solution?
- Disabled Glass Pane « Java Tips Weblog
コメント
- Tabキーで状態遷移しないようにするため、なにもしないFocusTraversalPolicyを追加しました。 -- aterai
- 相当悩みました。JDialog だと同じことができないのは何でなんでしょうねぇ。。。 -- おれ?
- カーソルが変わらないのでしょうか? それともコンパイルエラーが出るとかでしょうか? -- aterai
- 申し訳ない。カーソルが変わらないのだけれど、1.5系でコンパイルするとだめみたい。同じソースでも1.4系でコンパイルするとちゃんと変わる。1.5でのバグかな。。。 -- おれ?
- DefaultFocusTraversalPolicy を使うように変更しました。 -- aterai
- GlassPaneで、FocusTraversalPolicyを使わず、print を使ってMnemonicなどをブロックするように変更しました。 -- aterai
- SwingWorkerを使うように変更。 -- aterai