| ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 2007年08月05日 楽しいJava講座 - 初心者から達人へのパスポート vol.065 セルゲイ・ランダウ バックナンバー: http://www.flsi.co.jp/Java_text/ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ [このメールマガジンは、画面を最大化して見てください。] ======================================================== ◆ 01.グラフィックスのプログラミング ======================================================== 今回は、ClockAppletに海外の時刻も同時に表示できるようにし ましょう。 つまり、いわゆる世界時計の機能を追加することにします。 その前に前提知識を頭に入れておきましょう。 世の中には、「世界標準時」と「タイムゾーン(time zone:時刻帯)」 という言葉が存在しますね。 世界標準時は、元々はイギリスのグリニッジ(Greenwich)天文台 での天体観測によって計られていた時刻であり、グリニッジ標準時 (Greenwich Mean Time、略してGMT)と呼ばれていました(この言葉 自体は今でも使われています)。 しかし現在では、原子時計によって得られる正確な時刻に天文学的 に決める時刻を考慮して調整したUTC(英語=Coordinated Universal Time、 フランス語=Temps Universel Coordonne = 協定世界時)が世界の標準時 として使われるのが普通になりました。 現在では、GMTと表記しても実際はUTCの時刻を表しています。 ところで、地球の自転1回転が1日であることから、15度の回転ごとに 1時間経過することになり、経度が15度違うと時刻が1時間違うことに なりますね。 しかし実際には、あるまとまった地域では統一された同一の時刻が 使われるようになっており、たとえば中国のような広大な土地を持つ 国でも一つの統一された時刻が使われています。(中国の東端と西端 では、経度の違いだけで計算すると本来は4時間ほども時刻が違うべ きものなのです。一方、アメリカなどのいくつかの国では、同じ国内 でも地方によって異なる時刻が使われています。) このように、統一した時刻が使われる、まとまった地域のことをタイム ゾーン(time zone:時刻帯)といいます。また、その地域で統一された 時刻のことを「(地方の)標準時((Local) Standard Time)」と呼び ます。 地方の標準時はGMTから何時間進んでいるか、あるいは遅れているかで 表すこともでき、例えば3時間進んでいるのであればGMT+3、5時間遅れ ているのであればGMT-5というふうに表記することがあります。 詳しく知りたい人は、ウィキペディア(http://ja.wikipedia.org/) などを使って自分で調べてください。 では、プログラミングに入ることにしましょう。 最初に海外の時計を表示するためのJPanelを用意し、そこにClockPicture を組み込み、海外のタイムゾーンを選択するためのJComboBoxを貼り付け ることにします。 そして、選択されたタイムゾーンの時刻でClockPictureに時計を描かせる ことにします。 Eclipseを起動して、作業を始めましょう。 jp.co.flsi.lecture.clockパッケージの中に海外の時計を表示するための WorldClockJPanelというクラスをJPanelのサブクラスとして、ビジュアル・ クラス(Visual Class)として作成しましょう。 つまり、パッケージ・エクスプローラーの中のJStudy1の中のjp.co.flsi.lecture.clock を右クリックし、「新規」→「その他」→(「Java」の中の)「Visual Class」 を選択し「次へ」ボタンをクリックし、「名前」欄にWorldClockJPanelと入力し、 「Style」欄で(Swingの中の)Panelを選択しましょう。また、「インターフェース」欄 にRunnable(java.lang.Runnable)を追加しておきます。(これはclass文に 「implements Runnable」を指定することを意味します) そして、「Inherited abstract methods」にチェック・マークを入れておいてから、 「終了」ボタンをクリックします。 WorldClockPanelのVEエディターが開きましたら、最初にソース・コードの initialize()メソッドの中の -------------------------------------------------------- this.setSize(300, 200); -------------------------------------------------------- の行を -------------------------------------------------------- this.setSize(130, 140); -------------------------------------------------------- に変更しておきましょう。 (もしくは、「プロパティー」ビューでsizeプロパティーの値を130,140に 変更するというやり方もあります。) 続いて、このパネルのLayoutManagerをnullにしておきましょう。すなわち JPanelの画像を右クリックして「Set Layout」→「null」を選択します。 では、このパネルにClockPictureを組み込みましょう。 パレットから「Choose Bean」を選択し、「Choose a Bean」ウインドウにお いてClockPictureを入力して「OK」ボタンをクリックし、パネルの画像の外側 の白いところ(空白のエリア)をクリックすることによって貼りつけます。 Bean NameはclockPictureのままにしておきましょう。 そうするとgetClockPicture()というメソッドが自動的に生成されますね。 このメソッドを使えば、ClockPictureのインスタンスを取り出すことがで きます。 では、次にこのパネルにJComboBoxを貼り付けましょう。 パネル上の上端あたりに貼り付けてください。 Bean NameはjComboBoxTimeZoneにしておきましょう。 できるだけ(パネルの幅いっぱいまで)jComboBoxTimeZoneの幅を広げて おいてください。 次のようなgetJComboBoxTimeZone()メソッドが生成されていますね。 -------------------------------------------------------- private JComboBox getJComboBoxTimeZone() { if (jComboBoxTimeZone == null) { jComboBoxTimeZone = new JComboBox(); jComboBoxTimeZone.setBounds(new Rectangle(4, 4, 123, 25)); } return jComboBoxTimeZone; } -------------------------------------------------------- ただし、数字はjComboBoxTimeZoneの幅の広げ方によって異なります。 これを次のように編集しましょう。 -------------------------------------------------------- private JComboBox getJComboBoxTimeZone() { if (jComboBoxTimeZone == null) { jComboBoxTimeZone = new JComboBox(); jComboBoxTimeZone.setBounds(new Rectangle(4, 4, 123, 25)); String [] timeZones = TimeZone.getAvailableIDs(); for (String timeZone : timeZones) { jComboBoxTimeZone.addItem(timeZone); } } return jComboBoxTimeZone; } -------------------------------------------------------- ここでTimeZoneというのはタイムゾーンを表現するクラスで、java.utilパッ ケージにはいっています。 また、TimeZoneのgetAvailableIDs()というstaticメソッドはタイムゾーンの 識別名(time zone ID = タイムゾーンID)の文字列を配列にして返すメソッ ドで、これを使えばTimeZoneクラスで使用可能なタイムゾーンの名前のリス トが得られます。 ところで、JComboBoxに表示されるフォントのデフォルトはボールド(bold = 太字)でサイズが12ポイントになっていますが、これをplain(普通の文字) で10ポイントに変更することによって、なるべくたくさんの文字が表示でき るようにしておきましょう。 「Java Beans」ビューの中でjComboBoxTimeZoneをクリックし、 「プロパティー」ビューの中でfontプロパティーを選択してください。 その右側の値欄にDialog,bold,12という値が表示されているはずですが、 さらにその右端にあるボタンをクリックしてください。 「Java Property Editor」が開きますね。そこで、Styleはplainを選択し、 Sizeは10を選択して「OK」ボタンをクリックしてください。 終わったら保管(Ctrl + S)しておいてください。 パッケージ・エクスプローラーからWorldClockJPanelを右クリックし、 「実行」→「Java Bean」を選択して、どのように見えるか確認してみて ください。 フォントのサイズを6ポイントくらいに小さくすれば、すべてのタイムゾーン の名前が収まって表示できるようになると思いますが、小さすぎて読みにくく なります(というより、読めなくなります)。読みやすく、かつ、すべての タイムゾーンの名前が収まるようにするためには、パネル自体をもう少し広げ てやったほうがいいですが、面倒くさいので省略します。気になる人は自分で 調整してください。 では、このjComboBoxTimeZoneで選択したタイムゾーンの値を世界時計を表示 するときに渡せるようにフィールドに保管するようにしましょう。 そのために以下のフィールドの定義をWorldClockJPanelのソース・コードに 追加してください。 -------------------------------------------------------- private String timeZoneId = null; -------------------------------------------------------- 続いて、jComboBoxTimeZoneで選択が行われたら、その選択されたタイムゾーン がtimeZoneIdフィールドに代入されるようにプログラミングしましょう。 「Java Beans」ビューのjComboBoxTimeZoneを右クリックし、 「Events」→「actionPerformed」を選択します。 getJComboBoxTimeZone()メソッドの中に次のようなコードが自動生成されましたね。 -------------------------------------------------------- jComboBoxTimeZone.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed() } }); -------------------------------------------------------- これを次のように編集しましょう。 -------------------------------------------------------- jComboBoxTimeZone.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { timeZoneId = (String) jComboBoxTimeZone.getSelectedItem(); repaint(); } }); -------------------------------------------------------- 次に、時計の画像を描画するためにpaintComponent()メソッドを実装しましょう。 あとでこのパネルにJComboBoxを貼り付けますので、paint()メソッドではなく paintComponent()メソッドをオーバーライドしなければならないのです。 意味わかりますね。忘れた人は、vol.060を復習してください。 下記のようなpaintComponent()メソッドをWorldClockJPanelのソース・コードに 追加してください。 -------------------------------------------------------- public void paintComponent(Graphics g) { Image image = createImage(130, 140); Graphics2D g2dImage = (Graphics2D) image.getGraphics(); GregorianCalendar newTime = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId)); getClockPicture().setCurrentTime(newTime); getClockPicture().setCenterX(65); getClockPicture().setCenterY(85); getClockPicture().setRadius(35); getClockPicture().drawAll(g2dImage); g.drawImage(image, 0, 0, 130, 140, null); } -------------------------------------------------------- ここで、new GregorianCalendar(TimeZone.getTimeZone(timeZoneId))という ふうにGregorianCalendarのコンストラクターを呼び出してやると、 timeZoneIdに指定した識別名のタイムゾーンにおける現在時刻を持っ たGregorianCalendarインスタンスが生成されます。 あとは、ClockJPanelをプログラミングしたときとほとんど同じプログラ ミングで済むのですが、ただ、海外の時刻を示す時計は日本の時刻を示す時計 よりも小さくすることによって区別したいので、そのために半径と中心の座標 を調整しておきます。それが、ClockPictureのsetCenterX()メソッド、 setCenterY()メソッド、setRadius()メソッドを呼び出している理由です。 では、ClockJPanelをプログラミングしたときと同様に、 フィールド -------------------------------------------------------- private boolean runningFlag = false; -------------------------------------------------------- をWorldClockJPanelのソース・コードに追加しましょう。 そして、run()メソッド(メソッド・スタブが自動生成されている) を下記のように編集しましょう。 -------------------------------------------------------- public void run() { while(runningFlag) { repaint(); try { Thread.sleep(100); } catch(InterruptedException e) { } } } -------------------------------------------------------- またClockJPanelのプログラミングのときと同様に、 下記の2つのメソッド -------------------------------------------------------- public void clockStart() { runningFlag = true; Thread aThread = new Thread(this); aThread.start(); } -------------------------------------------------------- と -------------------------------------------------------- public void clockStop() { runningFlag = false; } -------------------------------------------------------- も WorldClockJPanelのソース・コードに追加しましょう。 では、WorldClockJPanelを保管しておいてから、ClockAppletの VEエディターを開いてください。 パレットから「Choose Bean」を選択し、「Choose a Bean」ウイン ドウにおいてWorldClockJPanelを入力して「OK」ボタンをクリックし、 アプレットの画像の中の空いているところ(前回AlarmJPanelを貼り付け たところの下)に貼り付け(クリック)してください。 Bean NameはworldClockJPanelのままにしておきましょう。 そうするとgetWorldClockJPanel()というメソッドが自動的に生成 されますね。 このメソッドの中の -------------------------------------------------------- worldClockJPanel.setBounds(new Rectangle(187, 83, 10, 10)); -------------------------------------------------------- という行(数字は貼り付け位置によって異なります)を -------------------------------------------------------- worldClockJPanel.setBounds(new Rectangle(170, 60, 130, 140)); -------------------------------------------------------- に書き換えましょう。 次に、ClockAppletのinit()メソッドとdestroy()メソッドを編集して WorldClockJPanelの時計のスレッドを起動するコードと停止するコード を追加しましょう。 すなわち、init()メソッドを -------------------------------------------------------- public void init() { this.setSize(300, 200); this.setContentPane(getJContentPane()); getClockJPanel().clockStart(); AudioClip audioClip = getAudioClip(getDocumentBase(), "alarm01.au"); getAlarmJPanel().setAudioClip(audioClip); getWorldClockJPanel().clockStart(); } -------------------------------------------------------- のように編集し、 destroy()メソッドを -------------------------------------------------------- public void destroy() { getClockJPanel().clockStop(); getWorldClockJPanel().clockStop(); } -------------------------------------------------------- のように編集しましょう。 (何を編集したのかわかりますね。それぞれgetWorldClockJPanel().clock... の行が追加されただけですよ。) では、ClockAppletを保管して起動し、テストしてみてください。 タイムゾーンをいろいろ選択してみてください。世界時計の時刻 が変わりますね。 ちなみに同じ標準時のタイムゾーンがいくつか並んでいたりするので、 タイムゾーンの選択を変えてみても時刻が変わらない場合がたくさん あります。 たとえば、Asia/TokyoとJST(=Japan Standard Time)とJapanは同じもの です。このうちのどれを選択しても日本の時刻になりますね。 また、中国の国内はどこでも同じ標準時を使っているのにそのタイムゾーン ときたら、CTT(China Taiwan Time)、PRC(People's Republic of China)か ら始まって、Asia/Shanghai(上海)、Asia/Harbin(ハルピン)、・・・と、 全部で11個くらいのタイムゾーン識別名があります。・・・・・ というふうに、このタイムゾーン識別名のリストは無駄が多いし、わかりにくい と感じるでしょうから、もっとわかりやすく整理されたリストを作ってみた い人は、自分でプログラムを改良してみてください。 なお、どのタイムゾーン識別名(Time zone ID)がどの地域のものかわからない という人は、下のリンクをクリックしてみてください。(下記リンクのWebページは 当メールマガジンとは何の関係もありません。) Time zone IDs ところで、この世界時計、午前と午後の区別がつきませんね。それに日付 もわからない。 WorldClockJPanelにもClockJPanelと同じように日付と現在時刻を文字列で 表示させるようにすればこの問題は解決しますが、それでは芸がない(と いうより、本来はClockJPanelを再利用したほうが速いのだが、ここでは練習 のために重複的でも手でプログラミングしておきたい)ので、日にちと AM/PMだけの表示を追加してみましょう。 また、ついでにAMかPMかによって世界時計の背景の色を変えるようにして、 世界時計の領域を飾りつけしてみましょう。 では、WorldClockJPanelのpaintComponent()メソッドを -------------------------------------------------------- public void paintComponent(Graphics g) { Image image = createImage(130, 140); Graphics2D g2dImage = (Graphics2D) image.getGraphics(); GregorianCalendar newTime = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId)); String ampm; Color backColor; if (newTime.get(Calendar.AM_PM) == Calendar.AM) { ampm = "AM"; backColor = Color.ORANGE; } else { ampm = "PM"; backColor = Color.CYAN; } g2dImage.setColor(backColor); g2dImage.fillRect(0, 0, 130, 140); g2dImage.setColor(Color.BLACK); g2dImage.drawString(ampm , 100, 50); g2dImage.drawString(newTime.get(Calendar.DAY_OF_MONTH) + "日" , 10, 50); getClockPicture().setCurrentTime(newTime); getClockPicture().setCenterX(65); getClockPicture().setCenterY(85); getClockPicture().setRadius(35); getClockPicture().drawAll(g2dImage); g.drawImage(image, 0, 0, 130, 140, null); } -------------------------------------------------------- のように編集してみましょう。面倒くさいので、paintComponent()メソッド のソース・コードをすべて提示しましたが、どこにどんなコードが追加され たのかわかりますね。 では、ClockAppletを保管して再度、起動し、テストしてみてください。 タイムゾーンをいろいろ選択してみてください。今度は世界時計に日にちと AM/PMの表示が出ますね。また、AMかPMかによって、世界時計の背景の色が 変わりますね。 (ちょっと色の趣味がよくなかったかも知れません。気になる人は自分で調 整してください。) では、今回はここまでにします。なお、今回作成したアプレットもホーム ページにも掲載しておきますので、完成したアプレットの動きを確認した い人は下記ホームページのvol.065の項目から実行してみてください。 http://www.flsi.co.jp/Java_text/ 何か、わからないところがありましたら、下記のWebページまで質問を お寄せください。 (続く) ======================================================== ◆ 02.Java(文法等)解説 [カスタム・タイムゾーンID] ======================================================== [カスタム・タイムゾーンID] タイムゾーンを指定するときに、今回のプログラムのようにタイム ゾーンの識別名を使わずに、GMTからの時間のずれで指定することも できます。 そのためには、カスタム・タイムゾーンIDを使用します。 カスタム・タイムゾーンIDは、 -------------------------------------------------------- GMT 符号 時 : 分 または、 GMT 符号 時 分 または、 GMT 符号 時 -------------------------------------------------------- の構文で記述されます。 (ここで、符号は + - のどちらか、時は1桁または2桁の数字、 分は2桁の数字です) たとえば、GMTより8時間早いタイムゾーンであれば"GMT+8:00" または"GMT+0800"または"GMT+8"と記述し、GMTより11時間遅れ たタイムゾーンであれば"GMT-11:00"または"GMT-1100"または "GMT-11"と記述します。 たとえば、日本の標準時はGMTより9時間進んでいますから、 TimeZone.getTimeZone("GMT+9") としてTimeZoneオブジェクトを作ってやればJSTと同じタイム ゾーンになります。 したがって、日本の標準時での現在時刻を表すGregorianCalendar インスタンスを生成するためには、 -------------------------------------------------------- new GregorianCalendar(TimeZone.getTimeZone("GMT+9")) -------------------------------------------------------- というコードでもかまわないのです。 ただし、日本のタイムゾーンは予めパソコンに設定されている (パソコンの設定が正しければ)ので、いちいちタイムゾーン を指定しなくてもデフォルトで日本のタイムゾーンが選ばれて います。したがって、 -------------------------------------------------------- new GregorianCalendar() -------------------------------------------------------- というコードでも日本のタイムゾーンが使用されるのです。 ちなみに英国(ロンドン)の標準時には夏時間(サマー・タイム、 イギリス英語ではsummer time、アメリカ英語ではdaylight saving time) がありますので、現在は(夏時間なので)GMTより1時間進んでいます。 GMTと同じではありませんので、注意してください。 (GMTには夏時間はありません。) 上のClockAppletに組み込んだ世界時計でEurope/Londonを指定すると、 GMTより1時間進んだ時刻になるはずです。つまり、このプログラムで 使ったタイムゾーンの識別名は夏時間も反映したものなのです (つまり、これを使えば夏時間も自動的に計算されるのです)。 その点、カスタム・タイムゾーンIDには夏時間の概念は含まれません ので、注意してください。 (続く) 以上、今回は ┌───────────────────────────┐ ・使用できるタイムゾーンの調べ方(TimeZoneのgetAvailableIDs()メソッドの使用方法) ・タイムゾーンを指定して現在時刻を取得する方法(new GregorianCalendar(TimeZone.getTimeZone(timeZoneId))) ・現在時刻のAM/PMの取得方法(Calendarのget(Calendar.AM_PM)メソッドの使用方法) ・描画の背景色の指定の仕方 ・カスタム・タイムゾーンIDの記述方法 └───────────────────────────┘ を学習しました。 では、また来週。 ================================================ ◆ 03.演習問題 ================================================ カスタム・タイムゾーンIDの記述方法を確認するために、下記のような プログラムを作成して実行してみてください。 -------------------------------------------------------- import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; public class TimeZoneTest { public static void main(String[] args) { GregorianCalendar newTime; newTime = new GregorianCalendar(); System.out.println("デフォルト"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("JST")); System.out.println("JST"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("GMT+9")); System.out.println("GMT+9"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("GMT")); System.out.println("GMT"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("Europe/London")); System.out.println("Europe/London"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("GMT+1")); System.out.println("GMT+1"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("GMT+01:00")); System.out.println("GMT+01:00"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); newTime = new GregorianCalendar(TimeZone.getTimeZone("GMT+0100")); System.out.println("GMT+0100"); System.out.println(newTime.get(Calendar.DAY_OF_MONTH) + "日" + newTime.get(Calendar.HOUR_OF_DAY) + "時" + newTime.get(Calendar.MINUTE) + "分"); } } -------------------------------------------------------- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ★ホームページ: http://www.flsi.co.jp/Java_text/ ★このメールマガジンは 「まぐまぐ(http://www.mag2.com)」 を利用して発行しています。 ★バックナンバーは http://www.flsi.co.jp/Java_text/ にあります。 ★このメールマガジンの登録/解除は下記Webページでできます。 http://www.mag2.com/m/0000193915.html ★このメールマガジンへの質問は下記Webページにて受け付けて います。わからない所がありましたら、どしどしと質問をお寄 せください。 http://www.flsi.co.jp/Java_text/ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ Copyright (C) 2007 Future Lifestyle Inc. 不許無断複製 |