javax.swing.Timer を使う

JavaのGUIアプリ用タイマーにはjavax.swing.Timerがある。

java.util.Timerとちがって、Swing用なのでGUIアプリを作成するときにしか利用できない。タイマーイベントという見え方なので、GUI プログラマにとって、イベントディスパッチスレッドを意識しなくてもよく、生成するスレッドの数が制限するための特別な処理をしなくても効率よくイベント処理を実行できるようになる。Java APIドキュメントによると「タイマーは、カーソルの点滅やツールヒントの表示などと同じスレッドを使用」ということである。詳細はUsing Timers in Swing Applicationsを見るとよいだろう。

簡単なカウントダウンプログラムを作成した。GUIアプリになると、いろいろな処理が増えるのでどうしてもコードが増えてしまう。ActionListenerを実装するクラスのオブジェクトをjavax.swing.Timerオブジェクトへ登録している点に注目しよう。また、終了時にはTimer.stop()を呼び出しておくのがよい。リソース解放についての説明がAPIドキュメントにはないが、不要になったらわかるようにしておくべきだろう。このTimerのために新しいスレッドが用意されるわけではなく他のGUI用スレッドと共有されるため、APIドキュメントでは「ただし、一方で Timer のアクションイベントハンドラは、GUI の応答を維持できるよう、迅速に動作する必要があります。」といった注意書きがある。自分が作成したActionListenerクラスで重たい処理をすると描画処理全体に影響が出るということになる。

package org.sssg.soft.sample.timer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SimpleSwingTimer extends JFrame {
  private static final long serialVersionUID = 1L;
  private final Timer timer;
  private final JLabel label = new JLabel();
  private final static int INTERVAL = 1000;

  class Task implements ActionListener {
    private int count = 10;
    @Override
    public void actionPerformed(ActionEvent e) {
      label.setText(count + " sec");
      if (count < 1) {
        timer.stop();
      } else {
        count--;
      }
    }
  }

  public SimpleSwingTimer(String title) {
    super(title);
    JPanel labelPanel = new JPanel();
    label.setPreferredSize(new Dimension(250, 20));
    labelPanel.add(label);
    Task task = new Task();
    timer = new Timer(INTERVAL, task);
    addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent e) {
        timer.stop();
        System.exit(0);
      }
    });
    getContentPane().add(labelPanel, BorderLayout.CENTER);
  }
  
  public void run() {
    timer.start();
  }

  private static void createAndShowGUI() {
    SimpleSwingTimer app = new SimpleSwingTimer("SimpleSwingTimer");
    app.pack();
    app.setLocationRelativeTo(null);
    app.setVisible(true);
    app.run();
  }

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

なお、Taskについては、次の用にjavax.swing.AbstractActionクラスを使う方法もある。

    timer = new Timer(INTERVAL, new javax.swing.AbstractAction() {
      private int count = 10;
      public void actionPerformed(ActionEvent e) {
        label.setText(count + " sec");
        if (count < 0) {
          timer.stop();
        } else {
          count--;
        }
      }
    });

Swingでは、マルチスレッドを意識する必要があるので、SwingUtilitiesのinvokeLaterメソッドや、EventQueueのinvokeLaterメソッドあたりも知っている必要がある。自分でThreadを生成して使うときには、これらの利用についても調べておくと良い。

同じタグの記事: Timer
同じカテゴリの記事: Java