広告 |
---|
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■ 2012年10月17日 Java総合講座 - 初心者から達人へのパスポート 2009年11月開講コース 065号 セルゲイ・ランダウ バックナンバー: http://www.flsi.co.jp/Java_text/ ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■ ------------------------------------------------------- ・現在、このメールマガジンは以下の2部構成になっています。 [1] 当初からのコース:vol.xxx(xxxは番号)が振られています。 これは現在、中級レベルになっています。 [2] 2009年11月開講コース:xxx号(xxxは番号)が振られています。 これは現在、初心者向けのレベルになっています。 ・このメールマガジンは、画面を最大化して見てください。 小さな画面で見ていると、不適切な位置で行が切れてしまう など、問題を起すことがあります。 ・このメールマガジンに掲載されているソース・コード及び 文章は特に断らない限り、すべて筆者が著作権を所有してい ます。また、これらのソース・コードは学習用のためだけに 提供しているものです。 ------------------------------------------------------- ======================================================== ◆ 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のサブクラスとして作成しましょう。 (1) パッケージ・エクスプローラーの中のJStudy1のsrc配下の jp.co.flsi.lecture.clock を右クリックし、「新規」→「その他」を選択します。 (2) 「新規」ウインドウにおい「WindowBuilder」配下の 「Swing Designer」配下の「JPanel」を選択し、「次へ」ボタンを クリックします。 (3) 「ソース・フォルダー」に「JStudy1/src」が入力されていること を確認し、「パッケージ」がjp.co.flsi.lecture.clockになっていること を確認し、「名前」の欄に WorldClockJPanel と入力しましょう。 (4) 「スーパークラス」が「javax.swing.JPanel」になっていることを確認し、 「完了」ボタンをクリックします。 WorldClockJPanelのエディターが開きましたら、class文に「implements Runnable」 を追加しておきましょう。つまり、ソース・コードを -------------------------------------------------------- package jp.co.flsi.lecture.clock; import javax.swing.JPanel; public class WorldClockJPanel extends JPanel implements Runnable { /** * Create the panel. */ public WorldClockJPanel() { } } -------------------------------------------------------- というふうに編集します。 そうすると、WorldClockJPanelに赤い下線が表示されますね。これは インターフェースRunnableのメソッドが実装されていないから (コンパイル)エラーになるためです。 そこで、WorldClockJPanelの部分にカーソルを入れてCtrl+1すると表示される メニューの中から「実装されていないメソッドの追加」を選択して下さい。 そうすると、ソース・コードが -------------------------------------------------------- package jp.co.flsi.lecture.clock; import javax.swing.JPanel; public class WorldClockJPanel extends JPanel implements Runnable { /** * Create the panel. */ public WorldClockJPanel() { } @Override public void run() { // TODO 自動生成されたメソッド・スタブ } } -------------------------------------------------------- というふうに、自動的にrun()メソッドが追加されますね。 続いて、Designビューを開き、このパネル(JPanel)を適切な サイズにしておきましょう。 JPanelの部分を選択(画面の中の灰色の部分をクリックするか、 または、Components欄の(javax.swing.JPanel)という文字列を クリックすると選択できる)し、Properties欄の 「Show advanced properties」ボタン(マウス・ポインターを のせると「Show advanced properties」と表示されるボタン) をクリックし、sizeプロパティーの値を(140,140)に変更して下さ い。(sizeプロパティーの右端の「・・・」ボタンをクリックし、 「サイズ」ウインドウで「幅」欄、「高さ」欄にそれぞれ、 140と140を入力して「OK」ボタンをクリックします。) 続いて、このパネルのLayoutManagerをnullにしておきましょう。すなわち JPanelの画像を右クリックして「Set Layout」→「Absolute layout」を 選択します。 では、このパネルにClockPictureを組み込みましょう。 Paletteから「Choose component」を選択し、「型を開く」ウインドウ において ClockPicture を入力して「OK」ボタンをクリックし、パネルの画像の外側の白い ところ(空白のエリア)をクリックすることによって貼りつけます。 そうするとソース・コードに -------------------------------------------------------- private final ClockPicture clockPicture = new ClockPicture(); -------------------------------------------------------- という行が自動生成されますね。 このclockPictureという変数を使えば、ClockPictureのインスタンスを 取り出すことができます。 では、次にこのパネルにJComboBoxを貼り付けましょう。 PaletteのComponentsの配下のJComboBoxを選択し、パネルの画像の 上端あたりに貼り付けてください。 変数名(Properties欄のVariable)は comboBoxTimeZone にしておきましょう。 できるだけ(パネルの幅いっぱいまで)comboBoxTimeZoneの幅を広げて おいてください。 ソース・コードに次のような行が生成されていますね。 -------------------------------------------------------- JComboBox comboBoxTimeZone = new JComboBox(); comboBoxTimeZone.setBounds(12, 0, 115, 15); add(comboBoxTimeZone); -------------------------------------------------------- ただし、数字はcomboBoxTimeZoneの幅の広げ方によって異なります。 これを次のように編集しましょう。 -------------------------------------------------------- JComboBox comboBoxTimeZone = new JComboBox(); comboBoxTimeZone.setBounds(12, 0, 115, 15); String [] timeZones = TimeZone.getAvailableIDs(); for (String timeZone : timeZones) { comboBoxTimeZone.addItem(timeZone); } add(comboBoxTimeZone); -------------------------------------------------------- ここでTimeZoneというのはタイムゾーンを表現するクラスで、java.utilパッ ケージにはいっています。 また、TimeZoneのgetAvailableIDs()というstaticメソッドはタイムゾーンの 識別名(time zone ID = タイムゾーンID)の文字列を配列にして返すメソッ ドで、これを使えばTimeZoneクラスで使用可能なタイムゾーンの名前のリス トが得られます。 ところで、JComboBoxに表示されるフォントのデフォルトのサイズが大きい (12ポイント)ですから、これを10ポイントに変更することによって、 なるべくたくさんの文字が表示できるようにしておきましょう。 「Properties」欄のfontの右側の値欄に MS UI Gothic 12という値が表示されているはずですが、さらにその右端 にある「・・・」ボタンをクリックしてください。 「Font chooser」ウインドウが開きますね。そこで、スタイルはplainの ままにし、サイズは10を選択して「OK」ボタンをクリックしてください。 終わったら保管(Ctrl + S)しておいてください。 パッケージ・エクスプローラーからWorldClockJPanelを右クリックし、 「実行」→「Java Bean」を選択して、どのように見えるか確認してみて ください。 では、このcomboBoxTimeZoneで選択したタイムゾーンの値を世界時計に 渡せるようにするために、フィールドに保管するようにしましょう。 まず、以下のフィールドの定義をWorldClockJPanelのソース・コードに 追加してください。 -------------------------------------------------------- private String timeZoneId = null; -------------------------------------------------------- 続いて、comboBoxTimeZoneで選択が行われたら、その選択されたタイムゾーン がtimeZoneIdフィールドに代入されるようにプログラミングしましょう。 DesignビューでcomboBoxTimeZoneを右クリックし、 「Add event handler」→「アクション」→「actionPerformed」を選択します。 ソース・コードに次のようなコードが自動生成されましたね。 -------------------------------------------------------- comboBoxTimeZone.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { } }); -------------------------------------------------------- これを次のように編集しましょう。 -------------------------------------------------------- comboBoxTimeZone.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { timeZoneId = (String) comboBoxTimeZone.getSelectedItem(); repaint(); } }); -------------------------------------------------------- すると、例によってcomboBoxTimeZoneをfinal指定していないために、エラー の赤い下線がつきますから、 -------------------------------------------------------- JComboBox comboBoxTimeZone = new JComboBox(); -------------------------------------------------------- の行にfinalを指定して -------------------------------------------------------- final JComboBox comboBoxTimeZone = new JComboBox(); -------------------------------------------------------- というふうに書き換えておきましょう。 次に、時計の画像を描画するためにpaintComponent()メソッドを実装しましょう。 あとでこのパネルにJComboBoxを貼り付けますので、paint()メソッドではなく paintComponent()メソッドをオーバーライドしなければならないのです。 意味わかりますね。忘れた人は、058号を復習してください。 下記のようなpaintComponent()メソッドをWorldClockJPanelのソース・コードに 追加してください。 -------------------------------------------------------- public void paintComponent(Graphics g) { Image image = createImage(130, 140); Graphics2D g2dImage = (Graphics2D) image.getGraphics(); GregorianCalendar newTime = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId)); clockPicture.setCurrentTime(newTime); clockPicture.setCenterX(65); clockPicture.setCenterY(70); clockPicture.setRadius(35); clockPicture.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()メソッド(メソッド・スタブが自動生成されている) を下記のように編集しましょう。 -------------------------------------------------------- @Override 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の ソース・コードを開いてください。 まず、WorldClockJPanelのオブジェクトを入れるフィールドとして下記の行を ソース・コードに追加して下さい。 -------------------------------------------------------- private WorldClockJPanel worldClockJPanel; -------------------------------------------------------- 続いて、ClockApplet()コンストラクターの最後に下記のようなコードを 追加しましょう。 -------------------------------------------------------- worldClockJPanel = new WorldClockJPanel(); worldClockJPanel.setBounds(170, 80, 130, 140); getContentPane().add(worldClockJPanel); -------------------------------------------------------- 次に、ClockAppletのinit()メソッドとdestroy()メソッドを編集して WorldClockJPanelの時計のスレッドを起動するコードと停止するコード を追加しましょう。 すなわち、init()メソッドを -------------------------------------------------------- public void init() { this.setSize(300, 200); this.setContentPane(getContentPane()); clockJPanel.clockStart(); AudioClip audioClip = getAudioClip(getDocumentBase(), "alarm01.au"); alarmJPanel.setAudioClip(audioClip); worldClockJPanel.clockStart(); } -------------------------------------------------------- のように編集し、 destroy()メソッドを -------------------------------------------------------- public void destroy() { clockJPanel.clockStop(); worldClockJPanel.clockStop(); } -------------------------------------------------------- のように編集しましょう。 (何を編集したのかわかりますね。それぞれworldClockJPanel.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, 30); g2dImage.drawString(newTime.get(Calendar.DAY_OF_MONTH) + "日" , 10, 30); clockPicture.setCurrentTime(newTime); clockPicture.setCenterX(65); clockPicture.setCenterY(70); clockPicture.setRadius(35); clockPicture.drawAll(g2dImage); g.drawImage(image, 0, 0, 130, 140, null); } -------------------------------------------------------- のように編集してみましょう。どこにどんなコードが追加されたのか わかりますね。 では、ClockAppletを保管して再度、起動し、テストしてみてください。 タイムゾーンをいろいろ選択してみてください。今度は世界時計に日にちと AM/PMの表示が出ますね。また、AMかPMかによって、世界時計の背景の色が 変わりますね。 (色の趣味がよくなかったかも知れませんが、気にいらない人は自分で調 整してください。) では、今回はここまでにします。なお、今回作成したアプレットもホーム ページにも掲載しておきますので、完成したアプレットの動きを確認した い人は下記ホームページの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-2012 Future Lifestyle Inc. 不許無断複製 |