Section 3.6 時計のディスプレイ

じゃ、今回は予告通り問題からね。
どんな問題なの?
ウィンドウの内部に、こんなふうに今の日付と時刻を表示するスクリプトを書いてみて。

<ウィンドウ内部のイメージ図>

ウィンドウ内部のイメージ図

えっと、上の段に日付と曜日を表示して、下の段に時刻を表示すればいいの?
うん、そう。
あと、この条件を全部みたすように作ってね。

<条件>

1. クラス名は ClockWindow にすること。

2. プライマリレイヤには frame.png を読み込むこと。

3. 日時表示用のレイヤを作って日時を書き込むこと。
   日時表示用のレイヤはプライマリレイヤの子レイヤにして、サイズは 114×68、位置は (22, 22) にすること。

4. フォントは『MS Pゴシック』で、高さは 24 ピクセルにすること。

5. 現在日時を表示するスクリプトはメソッドにする(コンストラクタから呼び出す)こと。

6. 分と秒の値は 10 未満の時 10 の位を 0 と表示すること(例:1 分の時は『01』と表示)。

7. 日時の文字列の上下の余白はすべて同じ高さにすること。
   左右の余白もすべて同じ幅にすること。

ちなみに 7 番目の条件は、下の図の同じ色の矢印の長さを同じにしてねってことだよ。

文字列の上下左右の余白

結構条件多いね…
けど今までやったことばっかりでしょ。
まぁそれはそうだけどねぇ。
でも最後の条件って難しそうだよ?
前回の文字を真ん中に表示するスクリプトを応用すればできるよ。
わからなさそうだったらヒント出すから。
う〜ん…ま、いっか。
じゃあ、やってみるね。
ん、がんばってね。
それじゃ、最初はコンストラクタから作るね。
条件 5 に書いてある通り、コンストラクタで日時を表示するメソッドを呼び出すから、 とりあえず日時を表示するための drawTime っていうメソッドがあるものとして、 コンストラクタ内で呼び出しといてね。
引数はどうすればいいの?
あ、別にこのメソッドには引数は要らないから無くていいよ。
おっけー。

<コンストラクタ>

function ClockWindow()
{
    super.Window();  // スーパークラスのコンストラクタを呼び出します

    add(new Layer(thisnull));  // プライマリレイヤを作ります

    // 条件 2:プライマリレイヤに画像を読み込みます(§3.3のスクリプトより抜粋)
    with(primaryLayer)
    {
        .loadImages("frame.png");   // プライマリレイヤに frame.png を読み込みます
        .setSizeToImageSize();      // プライマリレイヤのサイズを画像サイズに合わせます

        // ウィンドウのクライアント領域のサイズをプライマリレイヤのサイズに合わせます
        setInnerSize(.width, .height);
    }

    // 条件 3:日時を書くためのレイヤを作ります
    messageLayer = new Layer(this, primaryLayer);
    add(messageLayer);
    with(messageLayer)
    {
        .setPos(22, 22, 114, 68);        // 条件 3:サイズを 114×68、位置を (22, 22) にします
        .font.face = "MS Pゴシック";  // 条件 4:フォントを「MS Pゴシック」にします
        .font.height = 24;               // 条件 4:フォントの高さを 24 ピクセルに設定します
        .visible = true;
    }

    drawTime();  // 条件 5:日時を表示するメソッドを呼び出します

    visible = true;
}

えっと、こんな感じでどうかな…?
ん、バッチリだよ。これで条件 4 までクリアできたね。
うんっ!
んじゃ、次は日時を表示する drawTime メソッドね。
うん、じゃあ作ってみるね。
…と、その前に、文字を書く位置ってどうやって計算したらいいかよくわかんないんだけど…?
ん〜、じゃまず表示する文字列だけ作ってみて。
で、日付と曜日の方は date っていう変数に、 時刻の方は time っていう変数にそれぞれ代入してね。
りょーかい。

<日付・時刻の文字列を作るスクリプト>

var d = new Date();
var day_of_week = ["日""月""火""水""木""金""土"];

// 日付と曜日の文字列を作ります
var date = (d.getMonth() + 1) + "/" + d.getDate() + "(" + day_of_week[d.getDay()] + ")";

// 時刻の文字列を作ります
var time = d.getHours() + ":";

var min = d.getMinutes();
if(min < 10)
    time += "0";  // 時の値が 10 未満の場合は 10 の位を 0 にします
time += min + ":";

var sec = d.getSeconds();
if(sec < 10)
    time += "0";  // 分の値が 10 未満の場合は 10 の位を 0 にします
time += sec;

こんな感じだよね…?
ん、これで OK だよ。
でも、もっと簡単に書く方法があるんだ。
えっ、そうなの?
うん。
こうすればもっとシンプルに書けるよ。

<日付・時刻の文字列を作るスクリプト・改>

var d = new Date();
var day_of_week = ["日""月""火""水""木""金""土"];

// 日付と曜日の文字列を作ります
var date = (d.getMonth() + 1) + "/" + d.getDate() + "(" + day_of_week[d.getDay()] + ")";

// 時刻の文字列を作ります
var time = "%d:%02d:%02d".sprintf(d.getHours(), d.getMinutes(), d.getSeconds());

ほんとだ。時刻の文字列が1行で作れてるね。
でも、sprintf って見たことないメソッドだよ?
それに、文字列の後に直接 .sprintf って書いてあってなんかヘンな感じだし…
文字列に対するメソッドやプロパティは、こうやって文字列の後ろに直接書けるんだ。
だから、§1.11 で使った charAt メソッドや length プロパティとかもこう書けるよ。
へぇ〜、そうなんだ。
で、sprintf っていうのは文字列に対して使えるメソッドで、文字列を書式化できるんだ。
文字列を書式化…?
文字列の中に『%d』とか『%02d』って書いてあるでしょ。
うん。最初に %d って書いてあって、次に %02d で、 その後また %02d って書いてあるね。
これがそれぞれ引数の値に置き換わった文字列が time に代入されるんだ。
ってことは、最初の %dgetHours メソッドの戻り値に置き換わるってこと?
そ。
で、次の %02dgetMinutes メソッドの戻り値に置き換わって、 最後の %02dgetSeconds メソッドの戻り値に置き換わるの。
でも、それだったら普通に + 演算子で文字列の足し算すればいいんじゃないの?
それがそうでもないんだよね〜。
%d』っていうのは、そのまま getHours メソッドの戻り値に置き換わるんだけど、 『%02d』は getMinutesgetSeconds メソッドの戻り値が 10 未満だったら、10 の位に "0" を付け足した文字列に置き換わるんだ。
えっ、そうなの?
うん。
まず、『%d』ってのは、引数の値を整数で表した文字列に置き換わるんだ。
じゃあ、getHours メソッドの戻り値は整数だから、そのまま戻り値に置き換わるんだね。
ん、そう。
で、『%02d』だと、単に整数に置き換わるだけじゃなくて、「2桁以上の整数」に置き換わるんだ。
2桁以上の整数?
引数の値が2桁以上、つまり 10 以上だったらそのまま置き換わるんだけど、 もし引数の値が1桁しかなかったら、2桁になるように 10 の位に "0" を入れてくれるの。
へぇー、それって便利だねぇ。
でしょ。ちなみに、値がマイナスの場合は、-(マイナスの記号)も1桁と数えられるよ。
あと、『:』は置き換えには関係ないから、置き換え後も『:』のままなんだ。
なるほど、だから "%d:%02d:%02d" で時刻の文字列が作れるんだね。
他にも実数値に置き換えたり文字列に置き換えたりできるんだけど、今回は使わないから詳しい説明はパスするね。
リファレンスを参照、ってこと?
ん〜、sprintf っていうのは元々C言語っていうプログラミング言語で使われてる関数で、 TJS の sprintf メソッドの書式化のやり方はそれに従ってるから、 詳しく知りたい場合は、C言語の sprintf 関数を調べてみるといいんじゃないかな。
ふぅん、そうなんだ。
それじゃ、これで日付と時刻の文字列ができたし 6 番目の条件もクリアしたから、 次は書き込む位置を計算するね。
うん!
まず、x 座標の計算の仕方はわかるよね?
ちなみに x 座標は drawText の第1引数に指定する値のことね。
それって、この図の青と緑の矢印の長さってことだよね?

文字列の上下左右の余白

そうそう。
これは前回と同じように、レイヤの幅から文字列の幅を引いて2で割ればいいんじゃない?
うん、それでいいよね。
じゃあ、赤い矢印の高さはどうやって計算すればいいと思う?
それが難しいんだよねぇ…
レイヤの高さは『上の段の文字列の高さ+下の段の文字列の高さ+赤い矢印の高さ×3』になるよね。
えっ? え〜と…
うん、確かにそうなるね。
つまり、『レイヤの高さ=getTextHeight(上段の文字列)+getTextHeight(下段の文字列)+赤い矢印の高さ×3』っていう式が成り立つわけだから、 この式を使って赤い矢印の高さが計算できるよね?
そっか、『赤い矢印の高さ=(レイヤの高さ−getTextHeight(上段の文字列)−getTextHeight(下段の文字列))÷3』になるんだね。
ん、そういうこと。
で、上の段の文字列の y 座標は赤い矢印の高さの値と同じだから、 矢印の高さの値をそのまま drawText メソッドの第2引数に指定すればいいってわけだね。
なるほどね〜。
じゃあ、下の段の文字列の y 座標を計算してみて。
えっと、下の段の文字列の上には、上の段の文字列と赤い矢印が2つあるから…
getTextHeight(上段の文字列)+赤い矢印の高さ×2』…かな?
ん。そこまで解れば後は作れるよね。
うん、じゃあやってみるね!
あ、文字の色は黒だから、drawText メソッドの第4引数は 0x000000 でいいの?
うん、それでいいよ。

<現在の日時をレイヤに書き込む drawTime メソッド>

function drawTime()
{
    var d = new Date();
    var day_of_week = ["日""月""火""水""木""金""土"];

    // 日付と曜日の文字列を作ります
    var date = (d.getMonth() + 1) + "/" + d.getDate() + "(" + day_of_week[d.getDay()] + ")";

    // 条件 6:指定の書式で時刻の文字列を作ります
    var time = "%d:%02d:%02d".sprintf(d.getHours(), d.getMinutes(), d.getSeconds());

    with(messageLayer)
    {
        // 上段の文字列の幅と高さを取得します
        var dateWidth = .font.getTextWidth(date);
        var dateHeight = .font.getTextHeight(date);
        // 下段の文字列の幅と高さを取得します
        var timeWidth = .font.getTextWidth(time);
        var timeHeight = .font.getTextHeight(time);
        // 上段の文字列の y 座標(=赤い矢印の高さ)を計算します
        var dateTop = (.height - dateHeight - timeHeight) \ 3;

        // 条件 7:余白の幅、高さをそろえて上段の文字列を書き込みます
        .drawText((.width - dateWidth) \ 2, dateTop, date, 0x000000);
        // 条件 7:余白の幅、高さをそろえて下段の文字列を書き込みます
        .drawText((.width - timeWidth) \ 2, dateHeight + 2 * dateTop, time, 0x000000);
    }
}

うん、これで OK だと思うよ。
じゃあ、ここまでのスクリプトをまとめて実行してみよっか。
は〜い!
まず、ここまでのスクリプトをまとめるとこうなるね。

ClockWindow クラス>

class ClockWindow extends Window
{
    var messageLayer;  // 日時を書き込むためのレイヤ

    function ClockWindow()
    {
        super.Window();  // スーパークラスのコンストラクタを呼び出します

        add(new Layer(thisnull));  // プライマリレイヤを作ります

        // プライマリレイヤに画像を読み込みます
        with(primaryLayer)
        {
            .loadImages("frame.png");   // プライマリレイヤに frame.png を読み込みます
            .setSizeToImageSize();      // プライマリレイヤのサイズを画像サイズに合わせます

            // ウィンドウのクライアント領域のサイズをプライマリレイヤのサイズに合わせます
            setInnerSize(.width, .height);
        }

        // 日時を書くためのレイヤを作ります
        messageLayer = new Layer(this, primaryLayer);
        add(messageLayer);
        with(messageLayer)
        {
            .setPos(22, 22, 114, 68);        // サイズを 114×68、位置を (22, 22) にします
            .font.face = "MS Pゴシック";  // フォントを「MS Pゴシック」にします
            .font.height = 24;               // フォントの高さを 24 ピクセルに設定します
            .visible = true;
        }

        drawTime();  // 日時を表示するメソッドを呼び出します

        visible = true;
    }

    function finalize()
    {
        super.finalize();  // スーパークラスのデストラクタを呼び出します
    }

    // レイヤに日時を書き込むメソッド
    function drawTime()
    {
        var d = new Date();
        var day_of_week = ["日""月""火""水""木""金""土"];

        // 日付と曜日の文字列を作ります
        var date = (d.getMonth() + 1) + "/" + d.getDate() + "(" + day_of_week[d.getDay()] + ")";

        // 指定の書式で時刻の文字列を作ります
        var time = "%d:%02d:%02d".sprintf(d.getHours(), d.getMinutes(), d.getSeconds());

        with(messageLayer)
        {
            // 上段の文字列の幅と高さを取得します
            var dateWidth = .font.getTextWidth(date);
            var dateHeight = .font.getTextHeight(date);
            // 下段の文字列の幅と高さを取得します
            var timeWidth = .font.getTextWidth(time);
            var timeHeight = .font.getTextHeight(time);
            // 上段の文字列の y 座標(=赤い矢印の高さ)を計算します
            var dateTop = (.height - dateHeight - timeHeight) \ 3;

            // 余白の幅、高さをそろえて上段の文字列を書き込みます
            .drawText((.width - dateWidth) \ 2, dateTop, date, 0x000000);
            // 余白の幅、高さをそろえて下段の文字列を書き込みます
            .drawText((.width - timeWidth) \ 2, dateHeight + 2 * dateTop, time, 0x000000);
        }
    }
}

var win = new ClockWindow();  // ウィンドウを作ります

わぁ、結構長くなったねぇ。
だね。
じゃあ、実行してみて。
うん!

<実行結果>

現在日時が表示されているウィンドウ

やったぁ、ちゃんと表示されてるよ!
うん、うまくいったね!
なんかだいぶ時計っぽくなってきたよね〜。
まだ表示は止まったままだけど。
そだね。
あと表示を更新できるようになれば、ちゃんとした時計になるね。
それって次回やるの?
ううん、もうちょっと先かな。
表示を更新するためにはイベントを理解しなくちゃいけないから。
イベント?
というワケで、次回からイベントについて見ていくね。
それじゃ、また次回ね!


前へ | TOP | 次へ