第11回 GUI(グラフィカル・ユーザ・インターフェース)の設計 オブジェクト・プログラミング 第11回 GUI(グラフィカル・ユーザ・インターフェース)の設計
前回多かった質問 第6回で作った配列のsearchメソッドは、 public boolean search(int searchKey); という宣言でしたが、 第9回で作ったリストのsearchメソッドは、 public ItemInfo search(int searchKey); という宣言でした。 第10回の課題では、スーパークラスが、 public ItemInfo search(int searchKey); と宣言されているので、リストのほうは動くのですが、配列のほうのコンパイルが通りません。
前回の課題クラス図
前回多かった質問 解答(1) 第6回のメソッドは、検索し、値があるか無いかを調べるだけでしたので、返り値がbooleanでよかったのですが、第9回から、検索して、見つけたオブジェクトを返すように変更しました。 前回の課題は、配列、リストで実装した2つのFolderクラスをmainで性能評価する際に、同じスーパークラスのサブクラスとして、同じメソッドが使えるようにして、コードの重複を避けました。
前回多かった質問 解答(2) ですから、searchメソッドは同じ型のメソッド(同じ引数で、同じ返り値)でなければなりません。 前回多かった質問 解答(2) ですから、searchメソッドは同じ型のメソッド(同じ引数で、同じ返り値)でなければなりません。 ですから、前回の課題では、第6回で作ったメソッドの返り値をbooleanからItemInfoに変更し、見つけた商品情報を返り値として返すように変更する必要があります。
今日の目標 GUIを使った、簡単なプログラムが書けるようになる。 イベントドリブンの考え方について説明できるようになる Swingを使った簡単なGUIを作ることができるようになる。 イベントドリブンの考え方について説明できるようになる Javaでボタンを押されたときの動作をプログラミングすることができるようになる。 GUIの設計について説明できるようになる。 Model-View-Controller設計について説明できるようになる。
今日作る簡単な自販機 このウインドウが あなたのコンピュータ で開けるように、 プログラミングします。
今日作る自動販売機の仕様(1) ②投入金額表示 ウインドウの金額 が増える ①100円投入 ボタンが押される 今日は、いくら増えても かまわない。
今日作る自動販売機の仕様(2) ①ボタンを押す ②商品が出てくる 今日は、いくら商品が、 でてもかまわない。 投入金額と連携しなくて
JavaでGUIを取り扱う方法 Swingフレームワークを利用します。 フレームワークとは、アプリケーションを構築するための骨格を提供するコンポーネント群のこと。 フレームワークが提供されていれば、開発者は低レベルのコードを書かずにアプリケーションを構築することができる。 コンポーネントとは、「クラス利用の視点」によりオブジェクトを再利用する抽象的な単位をいう。 要するに、Swingで提供されているクラス群を使うと、簡単に、GUIなアプリケーションを作ることができます。 http://www.njk.co.jp/otg/Drop/Drop_v20/part1/chapter1.htmlより引用
Swingフレームワーク概要 すべてがオブジェクト! JFrame JLabel JTextField JButton
ウインドウを出してみよう! import javax.swing.*;//swingを使うときは宣言しなければならない import java.awt.*;//swingを使うときは宣言しなければならない //ただウインドウを出すだけのメインクラス public class SimpleWindowMain { public static void main(String args[]){ JFrame frame = new JFrame();//Swingで提供されるJFrameオブジェクト生成 frame.setTitle("初めてのウインドウ");//タイトル設定 frame.setSize(200,200);//大きさ設定 frame.setLocation(50,50);//位置設定 frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); //ウインドウが閉じたときにプログラムが終了するように設定 frame.setVisible(true);//表示する }
実行結果 次はこのウインドウにボタンを 乗せてみましょう!
カスタムウインドウを作る import javax.swing.*;//swingを使うときは宣言しなければならない import java.awt.*;//swingを使うときは宣言しなければならない //JFrameクラスを継承したカスタムウインドウクラス public class SimpleWindow extends JFrame{ //コンストラクタ public SimpleWindow(){ //ボタンの設定 JButton button = new JButton();//ボタンをインスタンス化 button.setText("初めてのボタン");//ボタンのラベル名設定 button.setSize(150,20);//ボタンの大きさ設定 button.setLocation(20,50);//ボタンの位置設定(ウインドウからの相対位置) getContentPane().setLayout(null);//ウインドウに載せられるすべてのオブジェクト //の位置を自分で設定できるようにする。 getContentPane().add(button);//ボタンをウインドウに乗せる }
mainの修正 import javax.swing.*;//swingを使うときは宣言しなければならない //カスタムウインドウを出すために修正したクラス public class SimpleWindowMain { public static void main(String args[]){ JFrame frame = new SimpleWindow();//オブジェクト生成 frame.setTitle(“初めてのカスタムウインドウ");//タイトル設定 frame.setSize(200,200);//大きさ設定 frame.setLocation(50,50);//位置設定 frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); //ウインドウが閉じたときにプログラムが終了するように設定 frame.setVisible(true);//表示する }
実行結果 ボタンが乗せられました。 しかし、押しても何も起こりません。 次は、ボタンが押されたとき、プログラムを 終了するようにしましょう!
ボタンが押されたときの処理を記述したい。 //まず、日本語でロジックを考えるための仮想言語です。 public void ボタンが押されたかどうかを調べて、押されていたら終了する(){ マウスの位置を調べる(); if(マウスの位置がButtonの中にある){ マウスの状態を調べる(); if(マウスが押されている){ if(押されているのは、マウスの左ボタンである){ System.exit(0);//プログラムを終了する。 } } このメソッドを一定期間ごとに動かせば、 一応できそうですね。
ハードウエアの状態を調べる 何処にいる? プログラム 動いてないよ! 押されてる? プログラム “a”が押されてます
しかし! 毎回毎回調べるのは大変で、時間がかかる。 もう一つボタンを作ったら、また、それが押されたかどうか調べるメソッドを用意して、一定期間ごとに調べなければならない。
そこで! イベント・ドリブンというプログラミングスタイルが考えられた。 処理をイベント毎に分割して記述して、 必要に応じて呼びだされ, 処理されるというものである。
イベント・ドリブンの考え方 ハードウエアを 常に見張っている 唯一のプログラム 押されたよ! プログラム プログラム 常に見張っている
用語 イベント・ディスパッチャー イベント イベント・ハンドラ ハードウエアを 常に見張っている 唯一のプログラム 押されたよ! プログラム
イベント・ドリブンプログラミング イベントドリブン型プログラムを作る時は、ただひたすら、イベントハンドラをどう書くかに集中すれば良い。 うれしい。 JavaのSwingフレームワークはこの機能を標準で持っています。 public void ボタンが押されたときの処理(){ System.exit(0); } これだけのメソッドを書けばよくなる。
Javaでイベントハンドラを作る JavaではイベントハンドラのことをEventListenerと呼び、オブジェクトとして表現します。
イベントハンドラのリスト ActionListenerは、 EventListenerを継承したクラス import java.awt.event.*; //Javaでのイベントハンドラの定義 public class SimpleButtonListener implements ActionListener{ public void actionPerformed(ActionEvent e){ System.exit(0); } ActionListenerは、 EventListenerを継承したクラス 「イベント」 を表わすオブジェクトが 引数になります。 メソッド名は決められて いるので間違えないように ※implementsの意味はここでは扱いません。 継承(extends)と似たような意味だという理解でよいです。
イベントハンドラを登録する //先ほどのプログラムのコンストラクタだけ抜粋 public SimpleWindow(){ JButton button = new JButton(); //大きさなどの設定は割愛 //イベントハンドラをインスタンス化 SimpleButtonListener listener = new SimpleButtonListener(); //イベントハンドラを受信者として登録する button.addActionListener(listener); //ウインドウにボタンを載せる処理を割愛 }
SimpleWindowの全リスト import javax.swing.*;//swingを使うときは宣言しなければならない import java.awt.*;//swingを使うときは宣言しなければならない import java.awt.event.*;//eventを使うときは宣言しなければならない //JFrameクラスを継承したカスタムウインドウクラス public class SimpleWindow extends JFrame{ //コンストラクタ public SimpleWindow(){ //ボタンの設定 JButton button = new JButton();//ボタンをインスタンス化 button.setText("初めてのボタン");//ボタンのラベル名設定 button.setSize(150,20);//ボタンの大きさ設定 button.setLocation(20,50);//ボタンの位置設定(ウインドウからの相対位置) button.addActionListener(new SimpleButtonListener());//ボタンを受信者登録 getContentPane().setLayout(null); getContentPane().add(button);//ボタンをウインドウに乗せる }
実行してみましょう! ボタンを押すと、ウインドウが 閉じて終了するはずです。
JBuilderを利用して設計する この作業を人力でやる のは、とても大変です。 JBuilderは、きっと、 あなたのプログラムを 助けてくれます。 しかし、とても重いです。 空のclass宣言を終わった 段階で、「設計」タブを クリックします。
JBuilderを利用したWindowのセッティング 右下の 「プロパティ」 から、layoutを nullに設定します。
何故Layoutをnullに設定するのか? 先ほども、 getContentPane().setLayout(null); というコードがでてきました。 Swingでは、標準で、ボタンの位置などを整形してくれるレイアウト機能がついています。 しかし、今回は、場所や大きさを自分で設定したいので、勝手にレイアウトされては困ります。 その機能を無効にするのが、このメソッドでレイアウトオブジェクトnullを設定する意味です。
JBuilderを利用したボタンの貼り付け Swingバーの Jbuttonをクリックし Frameキャンパスの設置 したい場所をクリックします。 コードが自動生成されます。 ソースコードに戻りたい場合は、 「ソース」タブをクリックします。
JBuilderを利用したイベントハンドラの登録 ②一番上の actionPerformed の右の空白を ダブルクリックします。 ①「イベント」タブを クリックします。
イベントハンドラの中身を書く イベントハンドラの外枠は自動生成されますが、中身は作ってくれません。(当然ですね。)自分でプログラムを書きます。
自動販売機用のコンポーネント オブジェクト・プログラミングの課題のために、自動販売機用のコンポーネントを作りました。 BackgroundPanelクラス 背景用のコンポーネントです。 この上に他の自販機コンポーネントを乗せる ことができます。 ItemInfoクラス 販売商品を表示するための コンポーネントです。
コンポーネント(続き) VMButtonクラス 自販機用のボタンです。 ProductOutletクラス 商品を最大10個 中に表示することができます。 CoinDepositクラス 投入金額を表示します。
カスタムコンポーネントの作成 この授業では、作られたコンポーネントを使って、プログラミングしてもらいます。 使うに当たり、中身がどのようにできているのか少し説明します。 金額表示ウインドウ(CoinDepositクラス)コンポーネントを例に説明します。
CoinDepositの構成 CoinDepositコンポーネントは、3つのクラスから成り立っています。 CoinDepositクラス CoinDepositModelクラス CoinDepositUIクラス 次ページ:クラス図
CoinDepositのクラス図
何故3つのクラスに分かれているか? CoinDepositの機能を実現するために一つのクラスで作ることもできます。 しかし、GUIのクラス設計では、責任ごとに3つのクラスに分割するのがよい方法とされています。 Swingの各コンポーネントも3つのクラスから同じ設計で作られています。 詳しく知りたいひとはデザインパターンなどのクラス設計法を勉強してみましょう。
GUIの基本クラス設計 Model-View-Controller設計 Model View 日本 = 100 アメリカ = 150 中国 = 130 Controller 日本 アメリカ 中国
各クラスの責任 Modelクラス Viewクラス Controllerクラス データ構造に責任を持つ 画面での表現に責任を持つ ユーザ入力に対するインターフェースに責任を持つ
何が嬉しいか?(1) View View 日本 アメリカ 中国 Model 日本 = 100 アメリカ = 150 中国 = 130 その逆も可能
何が嬉しいか?(2) Controller マウス・ イベントハンドラ Model 「メニュー」から やり直しを選ぶ 日本 = 100 アメリカ = 150 中国 = 130 キーボード・ イベントハンドラ Controller 様々なControllerに対して Modelの再利用が可能 また、その逆も可能 「Ctrl-Z」で ショートカット
もう一度クラス図を見てみよう! Controller Model View
簡単な実装の説明 CoinDepositModelクラス public class CoinDepositModel { private int amount; private CoinDeposit controller; public CoinDepositModel(CoinDeposit initController){ controller = initController; } public void addMoney(Money money){ amount = amount + money.getValue(); controller.repaint(); public int getAmount(){ return amount; public Money undo(){ //どうしたらできるか考えてみよう! ※コメントは省略 Model クラス 現在入れられたお金の 総量を計算して、 持っています。
簡単な実装の説明 CoinDepositUIクラス(1) Swingの 標準Viewクラス を継承します。 View クラス public class CoinDepositUI extends ComponentUI { private Image img; private int imgWidth = 160; private int imgHeight = 55; private String imgName = "image/coindeposit.gif"; public CoinDepositUI() { java.net.URL url = getClass().getClassLoader().getResource(imgName); img = Toolkit.getDefaultToolkit().getImage(url); } public Dimension getPreferredSize(JComponent component){ return new Dimension(imgWidth,imgHeight); 画像の読み込み を行ないます。 ※次ページに続く コンポーネントの推奨サイズを返すメソッドです。
簡単な実装の説明 CoinDepositUIクラス(2) Viewクラスは、 描画のアルゴリズム を備えています。 View クラス public void update(Graphics g,JComponent c){ paint(g,c); } public void paint(Graphics g,JComponent c){ CoinDepositModel model = ((CoinDeposit)c).getModel(); if(img != null){ g.drawImage(img,0,0,imgWidth,imgHeight,c); g.setColor(Color.red); g.setFont(new Font("Dialog",Font.PLAIN,32)); String amountStr = model.getAmount() + ""; g.drawString(amountStr,imgWidth/4,imgHeight*3/4); モデルから、 データを取ってきて、 描画します。
簡単な実装の説明 CoinDepositクラス Swingの 標準Controllerクラス を継承します。 簡単な実装の説明 CoinDepositクラス public class CoinDeposit extends JComponent { private CoinDepositModel model; public CoinDeposit() { model = new CoinDepositModel(this); setUI(new CoinDepositUI()); } public CoinDepositModel getModel(){ return model; Controller クラス すべての、 Controllerクラスは、 まだユーザの操作を 受け付けるように できていません。 これから皆さんがここにプログラムを書いていきます。
その他は質問してください。 優秀な(?)TA、SAに質問してください。 ソースにもコメントをたくさんつけてありますので、参照してください。
今日の課題(1) 授業の前半で説明した、シンプルなウインドウを表示し、ボタンを押すと、終了する簡単なプログラムを作りなさい。 提出すべきソース SimpleWindowクラス SimpleButtonListenerクラス SimpleWindowMainクラス
今日の課題(2) 最初に説明した仕様を満たす機能をもつ、簡単な自動販売機プログラムを作りなさい。 自動販売機コンポーネントを表示させる。 100円ボタンを押すと、金額表示ウインドウが更新される。 商品ボタンを押すと、商品が排出される。 お金が投入されていなくても出してよい。 何個排出されてもよい。 排出しても、投入金額を減らす必要はない。 やり方は、「課題の手順」にすべて書いてありますから、それどうりやってくれればかまいません。
発展課題 今日つかう、自動販売機プログラムのUMLクラス図を書きなさい。 手書きで書いて、 来週の授業の開始時に提出
提出方法 objprog-11@crew.sfc.keio.ac.jp宛て。 ただし、課題(2)のソースは、VendingMachineFrameだけでよい。 (感想を任意でお願いします。) Subjectはログイン名を必ず書いてください。 ex) t96844ym 余計な[]などをつけないでください。 来週の日曜(24:00)まで。(みんな、きつそうなので、変えました。がんばってください!)