countdownプログラムを作ってみよう6

   2012/05/05

こんにちは、管理人イガジーです。

最近のパソコンは高性能になっているので、少々無駄があるプログラムでも
ガンガン平気で動かすことができます。
Javaも昔は「重い」「遅い」と言われていましたが、Java自体の改良と
パソコンの高性能化のおかげで、軽々と動くようになりました。

速くではなく早く、つまり開発に時間をかけずに動かすため、あるいは
バグを生みにくくするために、少しくらいの無駄は気にしないというのが
最近の主流の考え方のようです。

管理人イガジーとしては、まずは無駄があっても、確実に動くものを作り
その後で、チューンナップ(無駄を削減し効率化)するのが良いと思っています。
(往々にして、チューンナップの方が時間がかかったりしますが)

さて、今日は、タイマーを使ってカウントダウンを自動更新するようにしてみましょう。

時計の描画のような周期的な処理をするには、
Timer クラスと TimerTask クラスを使います。

使い方は、次のようになります。

Timer tmr=new Timer();
tmr.schedule(new MyExtTimerTask(), 開始までの待ち時間(ms), 周期(ms));
...
class MyExtTimerTask extends TimerTask {
    public void run() {
// ここに周期的に行う処理を書く
    }
}

ここで、”MyExtTimerTask” の部分は好きな名称にしてください。

このタイマー処理を止めるには
tmr.cancel(); とします。

開始までの待ち時間は、1000(ms=1秒)くらいが良いでしょう。
周期は、あまり短いと無駄ですし、長いとそのぶん、日付が変わった時に
反映されるのに時間がかかります。

カウントダウンで秒まで表示する場合は、1000(以下)にします。

まずは、日にちの下に残り時間を時分秒で表示してみましょう。
既にBorderLayoutでの辺+中央は全て使っているので
下辺(SOUTH)にPanelを置いて、2つのラベルを格納します。

具体的にプログラム例を示しましょう。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;

public class CountDown {
	Calendar c,d;
	int cyy,cmm,cdd;
	int dbk=-1;
	long cms,dms;
	Timer tmr;

	Font f1=new Font("SansSerif",Font.PLAIN,20);
	Font f2=new Font("SansSerif",Font.PLAIN,16);
	Font f3=new Font("SansSerif",Font.PLAIN,72);

	Label today=new Label();
	Label rest=new Label();
	Label restime=new Label();

	CountDown(String [] as) { // constructor
		Frame f=new Frame();
		f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent ev) {
                System.exit(0);
            }
        });
		f.setSize(300,200);
		f.setTitle("CountDown");

		today.setFont(f2);
		today.setAlignment(Label.CENTER);
//		f.add(today, BorderLayout.SOUTH);
		Panel p2=new Panel();
		p2.setLayout(new GridLayout(2,1));
		restime.setFont(f1);
		restime.setAlignment(Label.CENTER);
		p2.add(restime);
		p2.add(today);
		f.add(p2, BorderLayout.SOUTH);

		Label target=new Label();
		target.setFont(f1);
		target.setAlignment(Label.CENTER);
		f.add(target, BorderLayout.NORTH);

		Label ll=new Label("あと");
		ll.setFont(f1);
//		ll.setAlignment(Label.RIGHT);
		f.add(ll, BorderLayout.WEST);
		f.add(target, BorderLayout.NORTH);
		Label rr=new Label("日");
		rr.setFont(f1);
		f.add(rr, BorderLayout.EAST);

		rest.setFont(f3);
		rest.setAlignment(Label.CENTER);
		f.add(rest, BorderLayout.CENTER);

		nowday();

		int dyy=cyy+1;
		int dmm=0;
		int ddd=1;
		if (as.length>0) {
			if (as[0].equals("-m")) {
				dyy=cyy;
				dmm=cmm+1;
				if (dmm>11) {++dyy; dmm=0;}
			}else{
				char c=as[0].charAt(0);
				int ymd=0;
				if ((c>='0')&&(c<='9')) {
					try {
						ymd=Integer.parseInt(as[0]);
					}catch(Exception e){
						ymd=0;
					}
					if (ymd!=0) {
						int y0=ymd/10000;
						dmm=(ymd%10000)/100 - 1;
						ddd=ymd%100;

						if (y0==0) {
							dyy=cyy;
							if ((dmm*100+ddd)<(cmm*100+cdd)) ++dyy;
						}
						else if (y0<100) dyy=(cyy/100)*100+y0;
						else if (y0>=cyy) dyy=y0;
					}
				} // end if a number is given
			}
		}
		d=new GregorianCalendar(dyy,dmm,ddd);
		upcount();

		if (as.length==0) target.setText("今年は");
		else if (as[0].equals("-m")) target.setText("今月は");
		else target.setText(String.format("%d年 %d月 %d日まで",
				d.get(Calendar.YEAR),
				d.get(Calendar.MONTH)+1,
				d.get(Calendar.DAY_OF_MONTH)));

		tmr=new Timer();
		tmr.schedule(new DoCount(), 1000, 1000);
//		tmr.schedule(new DoCount(), 1000, 1000*60);
		f.setVisible(true);
	} // end of constructor

	void nowday(){
		c=new GregorianCalendar();
		cyy=c.get(Calendar.YEAR);
		cmm =c.get(Calendar.MONTH);
		cdd =c.get(Calendar.DAY_OF_MONTH);
		cms=c.getTimeInMillis();
		String td=String.format("今日は %d年 %d月 %d日", cyy,cmm+1,cdd);
		today.setText(td);
	}

	void upcount(){
		dms=d.getTimeInMillis();
		int st=(int)((dms-cms)/1000); //秒
		int mm=st/60;  //分
		int hh=mm/60;  //時(hour)
		int dd=hh/24;  //日
		int ss=st%60;
		mm= mm%60;
		hh= hh%24;
		if (dd!=dbk) {
			String rd=Integer.toString(dd);
			rest.setText(rd);
			dbk=dd;
			if (st>0) rest.setBackground(Color.YELLOW);
			else rest.setBackground(Color.RED);
		}
		restime.setText(String.format("%d時間%2d分%2d秒", hh,mm,ss));
//		restime.setText(String.format("%d時間%2d分", hh,mm));
	}

	class DoCount extends TimerTask {
		public void run() {
			nowday();
			upcount();
		}
	}

	public static void main(String[] args) {
		new CountDown(args);
	}
}

この中で、
if (dd!=dbk) { ...}の部分は「日付が変化した時だけ描画する」という最適化処理です。
条件判断(if)をはずして、無条件に実行させても問題は無いのですが、
毎回描画しなおすのでチラツキが見えるかもしれません。

秒まで描画すると、落ち着かないという場合は

//		tmr.schedule(new DoCount(), 1000, 1000);
		tmr.schedule(new DoCount(), 1000, 1000*60);//		restime.setText(String.format("%d時間%2d分%2d秒", hh,mm,ss));
		restime.setText(String.format("%d時間%2d分", hh,mm));

とすれば良いでしょう。

概ねこれで完成ですが、人それぞれ好みがあると思いますので
色々と改造してみてください。

ご意見、ご質問などございましたら、コメントまたは
メールフォームをご利用ください。

ここまでのソースコードや実行プログラム(jar)は、リクエストがあれば
まとめてダウンロードできるようにしようと思っています。

countdownプログラムを作ってみよう1
countdownプログラムを作ってみよう2
countdownプログラムを作ってみよう3
countdownプログラムを作ってみよう4
countdownプログラムを作ってみよう5
countdownプログラムを作ってみよう6(この記事)

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。