広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2010年03月03日

    Java総合講座 - 初心者から達人へのパスポート
                  2009年11月開講コース 016号

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:毎週日曜の夜に発行
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:毎週水曜の夜に発行
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(2009年11月開講コース)016号
 当記事はvol.016のリバイバル(revival)版です。
 vol.016の内容を最新のEclipse、Javaに基づいて書き直しています。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


========================================================
◆ 01.ファイルへの入出力とメニュー・バー
========================================================

これまでは、画面上でデータを入力したり表示させたりするばかりで、データ
をファイルに保存するということを行っていませんでした。

そこで、これからHumanInfoWindowをさらに改良して、画面に入力した内容を
ファイルに書き出して保存したり、あるいは逆にファイルからデータを読み込
んで画面に表示できるようにしたいと思います。

ファイルに保存することを意味づけるためにHumanInfoWindowを「人事情報を
管理するためのソフト」と見なすことにしましょう。
つまり、画面に人事情報を入力して保存したり、保存してあるデータを取り出
して表示あるいは編集ができるようなソフトにします。
ただし、あくまでも入門者が学習するためのサンプルのソフトですから、いき
なり複雑なアプリケーションを作ろうとしたら混乱してしまいます。
そこで、氏名と生年月日と年齢だけの簡単なデータを扱うものとします。

ファイルへの保存とかファイルからのデータの読み込みと言ったら、メニュー・
バーに「ファイル」というメニューがあって、その中の「開く」というメニュ
ー項目や「保存」というメニュー項目を選択して行うことが多いですね。
そこで、HumanInfoWindowにも「ファイル」メニューを作り込んでみましょう。

メニューを作るためには、主にMenuBarとMenuとMenuItemという3種類のGUI部品
を使います。(メニュー関連のGUI部品は、ほかにもあります。)
MenuBarは文字通りメニュー・バーを表すクラスで、Menuは文字通りメニューを
表すクラスで、MenuItemは文字通りメニュー項目を表すクラスです。
そして、MenuBarの中にMenuが組み込まれ、Menuの中にMenuItemが組み込まれます。

ただし、実はMenuはMenuItemの一種です。つまりMenuはMenuItemのサブクラスに
なっています。
MenuがMenuItemの一種だということは、Menuの中にはMenuItemだけでなくMenuも
組み込むことができるということです。そしてそのMenuの中にもさらにMenuが組
み込めることになります。
つまり、Menuを開くとその中にさらにMenuがあって、そのMenuを開くとさらにMenu
があって、そのMenuを開くとさらに・・・・といったメニューの階層構造も作れる
ということです。
これだけの説明では、何のことやらわからないかもしれませんが、また必要になっ
たときに詳しく説明しますので、ご安心ください。


さて今回は、とりあえず「ファイル」というMenuの中に「ファイルを開く」「ファイ
ルを閉じる」「名前をつけて保存」「上書き保存」「終了」という5つのメニュー
項目を組み込むことにしましょう。
しかし「ファイル」メニューだけでは、ちょっと貧相で格好悪いので、ついでに
「編集」メニューも用意し、「編集」メニューには「データ・クリア」というメニュー項目
だけを組み込むこととします。
そして、今回はとりあえず、「ファイル」メニューの「終了」をクリックすると
アプリケーションを終了するという機能だけを実装しておきます。

ファイルへの入出力の話は次回行います。


はい。こちらに、既にメニューを組み込んだソース・コードを用意しておきました。

-------------------------------------------------------
package jp.co.flsi.lecture.ui;

import java.awt.Button;
import java.awt.Choice;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import jp.co.flsi.lecture.human.HumanInfo;
import jp.co.flsi.lecture.util.CalendarConverter;

public class HumanInfoWindow extends Frame {
   private HumanEventListener eventListener = new HumanEventListener();
   private HumanActionListener actionListener = new HumanActionListener();
   private Label labelDateOfBirth = new Label();
   private Choice nengoChoice = new Choice();
   private Label labelYear = new Label();
   private Label labelMonth = new Label();
   private Label labelDay = new Label();
   private Label labelName = new Label();
   private Label labelResponse = new Label();
   private Button instanceButton = new Button();
   private Label labelAge = new Label();
   private TextField textFieldYear = new TextField();
   private TextField textFieldMonth = new TextField();
   private TextField textFieldDay = new TextField();
   private TextField textFieldName = new TextField();
   private HumanInfo humanInfo = null;
   private CalendarConverter calendarConverter = null;
  
   class HumanEventListener implements WindowListener {
      public void windowActivated(WindowEvent e) {};
      public void windowClosed(WindowEvent e) {
         System.exit(0);
      };
      public void windowClosing(WindowEvent e) {
         Frame frame = (Frame)e.getSource();
         frame.setVisible(false);
         frame.dispose();
      };
      public void windowDeactivated(WindowEvent e) {};
      public void windowDeiconified(WindowEvent e) {};
      public void windowIconified(WindowEvent e) {};
      public void windowOpened(WindowEvent e) {};
   };
  
   class HumanActionListener implements ActionListener {
      public void actionPerformed(ActionEvent e) {
         Button button = (Button)e.getSource();
         HumanInfoWindow window = (HumanInfoWindow)button.getParent();
         int nengoIndex = nengoChoice.getSelectedIndex();
         int year = Integer.parseInt(window.getTextFieldYear().getText());
         int month = Integer.parseInt(window.getTextFieldMonth().getText());
         int day = Integer.parseInt(window.getTextFieldDay().getText());
         HumanInfo human = window.getHumanInfo();
         CalendarConverter calendarConverter = window.getCalendarConverter();
         if (nengoIndex < 4) {
            calendarConverter.setJaNameOfEra(nengoIndex);
            calendarConverter.setJaYear(year);
            calendarConverter.convertJaCal2GreCal();
            year = calendarConverter.getGreYear();
         }
         human.setDateOfBirth(year, month, day);
         human.setName(window.getTextFieldName().getText());
         window.getLabelResponse().setText(human.getAge() + "歳"); 
      };
   };
  
   class HumanMenuActionListener implements ActionListener {
      private MenuItem menuItem;
      private HumanInfoWindow window;
      public HumanMenuActionListener(HumanInfoWindow window) {
         this.window = window;
      };
      public void actionPerformed(ActionEvent e) {
         menuItem = (MenuItem)e.getSource();
         if ("終了".equals(menuItem.getLabel())) {
            window.setVisible(false);
            window.dispose();
         }
      };
   };
  
   public HumanInfoWindow()  {
      super();
      setTitle("人事情報"); 
      setSize(400, 180);
      setLayout(null);
      Menu fileMenu = new Menu("ファイル"); 
      Menu editMenu = new Menu("編集"); 
      MenuItem openMItem = new MenuItem("ファイルを開く"); 
      MenuItem closeMItem = new MenuItem("ファイルを閉じる"); 
      MenuItem nmsaveMItem = new MenuItem("名前をつけて保存"); 
      MenuItem saveMItem = new MenuItem("上書き保存"); 
      MenuItem exitMItem = new MenuItem("終了"); 
      fileMenu.add(openMItem); 
      fileMenu.add(closeMItem); 
      fileMenu.add(nmsaveMItem); 
      fileMenu.add(saveMItem); 
      fileMenu.add(exitMItem); 
      MenuItem clearMItem = new MenuItem("データ・クリア"); 
      editMenu.add(clearMItem); 
      MenuBar menuBar = new MenuBar(); 
      menuBar.add(fileMenu); 
      menuBar.add(editMenu); 
      setMenuBar(menuBar); 
      labelDateOfBirth.setBounds(10, 60, 80, 20); 
      nengoChoice.setBounds(120, 60, 80, 20); 
      nengoChoice.add("明治");
      nengoChoice.add("大正");
      nengoChoice.add("昭和");
      nengoChoice.add("平成");
      nengoChoice.add("西暦");
      getTextFieldYear().setBounds(210, 60, 40, 20); 
      labelYear.setBounds(255, 60, 20, 20); 
      getTextFieldMonth().setBounds(290, 60, 20, 20); 
      labelMonth.setBounds(315, 60, 20, 20); 
      getTextFieldDay().setBounds(350, 60, 20, 20); 
      labelDay.setBounds(375, 60, 20, 20); 
      labelName.setBounds(10, 90, 60, 20); 
      getTextFieldName().setBounds(70, 90, 320, 20); 
      instanceButton.setBounds(120,120, 160, 20); 
      labelAge.setBounds(10, 150, 100, 20); 
      getLabelResponse().setBounds(110, 150, 100, 20); 
      add(labelDateOfBirth);
      add(nengoChoice);
      add(getTextFieldYear());
      add(labelYear);
      add(getTextFieldMonth());
      add(labelMonth);
      add(getTextFieldDay());
      add(labelDay);
      add(labelName);
      add(getTextFieldName());
      add(instanceButton);
      add(labelAge);
      add(getLabelResponse());
      labelDateOfBirth.setText("生年月日:");
      labelYear.setText("年");
      labelMonth.setText("月");
      labelDay.setText("日");
      labelName.setText("氏名:");
      instanceButton.setLabel("年齢計算");
      labelAge.setText("現在の年齢:"); 
      instanceButton.addActionListener(actionListener);
      HumanMenuActionListener menuActionListener = new HumanMenuActionListener(this); 
      exitMItem.addActionListener(menuActionListener); 
      addWindowListener(eventListener);
      setVisible(true);
   }

   public TextField getTextFieldYear() {
      return textFieldYear;
   }

   public TextField getTextFieldMonth() {
      return textFieldMonth;
   }

   public TextField getTextFieldDay() {
      return textFieldDay;
   }

   public TextField getTextFieldName() {
      return textFieldName;
   }

   public Label getLabelResponse() {
      return labelResponse;
   }
  
   public HumanInfo getHumanInfo( ) {
      if (humanInfo == null) humanInfo = new HumanInfo();
      return humanInfo;
   }
  
   public CalendarConverter getCalendarConverter( ) {
      if (calendarConverter == null) calendarConverter = new CalendarConverter();
      return calendarConverter;
   }
  
   public static void main(String[] args) {
      HumanInfoWindow frame = new HumanInfoWindow();
   }

}
-------------------------------------------------------

とりあえず、これをEclipseで入力してみて、実行して動きを確認してみてくだ
さい。
メニューが表示され、「ファイル」→「終了」と選択するとアプリケーションが
終了しますね。


それでは、新しく組み込まれたコードを説明していきます。

95行目〜96行目
-------------------------------------------------------
Menu fileMenu = new Menu("ファイル");
Menu editMenu = new Menu("編集");
-------------------------------------------------------
は、「ファイル」メニューのインスタンスと「編集」メニューのインスタンスを
生成しています。

97行目〜101行目
-------------------------------------------------------
MenuItem openMItem = new MenuItem("ファイルを開く"); 
MenuItem closeMItem = new MenuItem("ファイルを閉じる"); 
MenuItem nmsaveMItem = new MenuItem("名前をつけて保存"); 
MenuItem saveMItem = new MenuItem("上書き保存"); 
MenuItem exitMItem = new MenuItem("終了"); 
-------------------------------------------------------
は、「ファイル」メニューに組み込むメニュー項目のインスタンスを生成しています。

102行目〜106行目
-------------------------------------------------------
fileMenu.add(openMItem); 
fileMenu.add(closeMItem); 
fileMenu.add(nmsaveMItem); 
fileMenu.add(saveMItem); 
fileMenu.add(exitMItem); 
-------------------------------------------------------
は、それらのメニュー項目を「ファイル」メニューに組み込んでいます。見て
の通り、この組み込みは(Menuクラスの)add()メソッドで行います。このメソ
ッドの引数はMenuItemクラスのインスタンスです。

107行目
-------------------------------------------------------
MenuItem clearMItem = new MenuItem("データ・クリア");
-------------------------------------------------------
は、「編集」メニューに組み込むメニュー項目のインスタンスを生成しています。

108行目
-------------------------------------------------------
editMenu.add(clearMItem);
-------------------------------------------------------
は、そのメニュー項目を「編集」メニューに組み込んでいます。

109行目
-------------------------------------------------------
MenuBar menuBar = new MenuBar();
-------------------------------------------------------
は、メニュー・バーのインスタンスを生成しています。

110行目〜111行目
-------------------------------------------------------
menuBar.add(fileMenu);
menuBar.add(editMenu);
-------------------------------------------------------
は、それぞれ「ファイル」メニューと「編集」メニューをメニュー・バーに組み
込んでいます。見ての通り、この組み込みは(MenuBarクラスの)add()メソッド
で行います。このメソッドの引数はMenuクラスのインスタンスです。

112行目
-------------------------------------------------------
setMenuBar(menuBar);
-------------------------------------------------------
は、このメニュー・バーをHumanInfoWindowのウインドウに取り付けています。
見ての通り、この取り付けは(Frameクラスの)setMenuBar()メソッドで行いま
す。このメソッドの引数はMenuBarクラスのインスタンスです。

以上で、メニュー・バー、メニュー、メニュー項目がウインドウに組み込まれま
した。


なお、このメニュー・バーが入り込んだ分、以前から貼り付けてあるGUI部品の
位置をずらす必要がありますので、それぞれのsetBounds()メソッドの引数の数値
を変更していることも確認してください。基本的にはすべて下の方向(y方向)に
30ピクセルずつずらしてあります。(113〜114行目および120〜130行目)

また、それに合わせて、全体のウインドウのサイズも縦方向に3030ピクセルだけ大
きくしてあります。(93行目のsetSize()メソッド)

それから、人事情報を管理するソフトらしく見せるためにタイトル・バーの表示を
「人事情報」に変更しています。(92行目のsetTitle()メソッド)

また、同じく人事情報らしく見せるために年齢を表示するところに新たに「現在の
年齢:」と表示するラベル(Labelクラスのインスタンス)を追加しています。
(29,129,142,150行目のlabelAge変数)

そして71行目
-------------------------------------------------------
window.getLabelResponse().setText(human.getAge() + "歳");
-------------------------------------------------------
は以前、劇場のイメージで話言葉風の文にしていたものを単に「何歳」と表示する
だけに変更することによって、人事情報管理ソフトらしくしています。


それでは、次にメニューの「ファイル」→「終了」を選択したときにアプリケーシ
ョンを終了するためのコードですが、まず75〜88行目
-------------------------------------------------------
class HumanMenuActionListener implements ActionListener {
   private MenuItem menuItem;
   private HumanInfoWindow window;
   public HumanMenuActionListener(HumanInfoWindow window) {
      this.window = window;
   }
   public void actionPerformed(ActionEvent e) {
      menuItem = (MenuItem)e.getSource();
      if ("終了".equals(menuItem.getLabel())) {
         window.setVisible(false);
         window.dispose();
      }
   };
};
-------------------------------------------------------
は、このメニュー項目をクリックしたときのイベントを受け取るためのアクション
・リスナーとして定義したクラスです。
ボタン(Button)の場合と同じくactionPerformed()メソッドが呼び出されることに
なりますので、actionPerformed()メソッドの中で終了の処理をプログラミングすれ
ばいいわけです。
処理内容は、「閉じる」ボタンをクリックしたときの処理と同じです。
なお、このHumanMenuActionListenerというクラスは、MenuItemのイベントを受け
取るための専用のアクション・リスナーとして定義しました。
したがって、そのイベントのソース(源)がMenuItemであることは予めわかって
いますから、82行目のように(MenuItem)でキャストし、MenuItemのインスタンス
として扱えるようにしています。

83行目
-------------------------------------------------------
if ("終了".equals(menuItem.getLabel())) {
-------------------------------------------------------
ではそのMenuItemのインスタンス(メニュー項目)のラベル(表示している文字列)
が「終了」であることを確認していますが、これは後に「終了」以外のメニュー項目
をクリックしたときにもこのアクション・リスナーを使う予定であるため、「終了」
以外のメニュー項目と振り分けられるようにしておいたものです。

ところで、78〜80行目
-------------------------------------------------------
public HumanMenuActionListener(HumanInfoWindow window) {
   this.window = window;
}
-------------------------------------------------------
ではHumanMenuActionListenerのコンストラクターを定義しており、引数を
HumanInfoWindow型にしています。
HumanActionListenerなど、これまでに定義してきたリスナーのクラスにはこのよう
なコンストラクターの定義は無かったですね。
今回、なぜこんなコンストラクターを定義したかというと、イベントの発生元のウイ
ンドウにアクセスするための手順を簡単にするためです。
HumanActionListenerでは、56行目
-------------------------------------------------------
HumanInfoWindow window = (HumanInfoWindow)button.getParent();
-------------------------------------------------------
のようにgetParent()メソッドを使ってButtonオブジェクトの親を取り出すと、それ
がすなわちHumanInfoWindowのウインドウだったのですが、MenuItemオブジェクトの
場合はその親はMenuオブジェクトで、さらにその親がMenuBarオブジェクトで、さら
にその親がやっとHumanInfoWindowのウインドウということになり、ウインドウにた
どり着くのが面倒くさいのです。
一応そのコードを書いてみると、たとえば
-------------------------------------------------------
window = (HumanInfoWindow)((MenuBar)((Menu)menuItem.getParent()).getParent()).getParent();
-------------------------------------------------------
という感じになります。まだ、この程度なら何とかなりますが、今後もっと複雑な
パターンが出てきたら、大変わかりずらくなります。

そこで、もっと簡単でわかりやすい方法として、コンストラクターを利用してウイ
ンドウのオブジェクトを渡す方法を示しました。
79行目
-------------------------------------------------------
this.window = window;
-------------------------------------------------------
ではコンストラクターの引数として受け取ったwindowをthis.windowに代入していま
す。このthisはHumanMenuActionListenerのインスタンス自身を示しますから、this.windowは77行目
-------------------------------------------------------
private HumanInfoWindow window;
-------------------------------------------------------
のwindow変数を示します。

そして、このコンストラクターは152行目
-------------------------------------------------------
HumanMenuActionListener menuActionListener = new HumanMenuActionListener(this);
-------------------------------------------------------
でHumanMenuActionListenerのインスタンス生成をするときに呼び出されています。
このHumanMenuActionListener(this)のthisはHumanInfoWindowのインスタンス自身を
表しますね。
つまり、自分自身のアドレスをHumanMenuActionListenerのコンストラクターの引数
として渡してHumanMenuActionListenerのインスタンスを生成しているわけです。

そうして、153行目
-------------------------------------------------------
exitMItem.addActionListener(menuActionListener);
-------------------------------------------------------
によって、このHumanMenuActionListenerのインスタンスを「終了」のメニュー項目の
イベント・リスナーとして登録しています。

こうすると、84〜85行目
-------------------------------------------------------
window.setVisible(false);
window.dispose();
-------------------------------------------------------
のwindowはコンストラクターの引数として渡されたHumanInfoWindowのインスタンスを
表すことになり、getParent()メソッドを何回も呼び出さなくても簡単にウインドウに
アクセスできます。



========================================================
◆ 02.文法解説 [リテラル]
========================================================

データの値をプログラムのソース・コード内に直接記述したものを「リテラル」(literal)と呼びます。
Javaではリテラルを次のようにして記述することができます。

(1) 整数(integer)
(10進数表記の場合)
数字をそのまま書いて表現します。例えば、
   5 あるいは 137
等はすべて10進数表記の整数(int型)となります。また、末尾にl(エル)またはLを
付けるとlong型を表わします。例えば、
   1278lあるいは1278L
とするとlong型になります。

(8進数表記の場合)
先頭を0(ゼロ)で始めると、8進数表記の整数となります。例えば、
   0137
は8進数です。

(16進数表記の場合)
先頭を0xまたは0Xで始めると、16進数表記の整数となります。例えば、
   0XDE2F または 0xde2f
はどちらも同じ16進数表記の整数です。

(2) 浮動小数点数(floating point number)
以下の表記は、いずれも浮動小数点数(double型)を表わします。
   2.718
   3.5E12
   3.5E+12
   3.5E-12
   5e11
eまたはEに続く符号と数字が指数部です。また、末尾にdまたはDを付けるとdouble型
を表わし、末尾にfまたはFを付けるとfloat型を表わします。末尾に何もつけない時
は、doubleとして扱われます。

(3) 文字(character)
文字は' 'で囲みます。例えば、
   'a'
というふうに1文字を' 'で囲んで表現します。Javaの内部コードではUnicode(UCS-2)が使
われており、ここに指定できる1文字は2バイトの文字です。したがって、アルファベット
と同様に漢字1文字も指定できます。この点はC++言語と異なりますので注意してくだ
さい。また、文字と同様に
   '\n'  (改行を意味するエスケープ・シーケンス)
のようにして、エスケープ・シーケンス*も指定できます。
(*)エスケープ・シーケンスとは、コンピューターに特殊な機能を実行させる文字コー
ドであり、他に以下のようなものがあります。

               Unicode値                   説明
 '\u????'       ????                     Unicodeエスケープ(*)による文字の指定
 '\b'            0008                     バックスペース
 '\t'            0009                     HT(水平タブ)
 '\n'            000a                     LF(改行)
 '\f'            000c                     FF(改ページ)
 '\r'            000d                     CR(復帰)
 '\"'            0022                     二重引用符
 '\''            0027                     単一引用符
 '\\'            005c                     円記号

(*)UnicodeエスケープというのはUnicodeの文字をASCIIで指定するためのものです。
(詳しくは後述。)

(4) 文字列(string)
文字列のリテラルは、
   "abcdefg1234は文字列です。"
のように、二重引用符(")2つで囲んで表現します。

(5) ブーリアン(boolean)
以下の2種類の値をリテラルで指定することができます。
false (偽を意味する)
true (真を意味する)


では、今回はここまでにします。



================================================
◆ 03.前回の演習問題の答
======================================================

String  s  = "a";
とします。

まちがえて
String  s  = 'a';
としてしまうとコンパイル・エラーになってしまいます。
今回のリテラルのお話でも出てきたように、String型の場合は""で囲みます。
たとえ1文字だけの場合でもです。
C++とは違いますので注意してください。

なお、以前にもお話したように、""で囲んだものはStringのインスタンスになります。
'a'とするとプリミティブ型となるのに対し、""で囲んだものはオブジェクトになるわけです。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      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) 2010 Future Lifestyle Inc. 不許無断複製