第14回 GUIの構成とイベント・ドリブン

第14回 GUIの構成とイベント・
ドリブン
~GUIを使ったプログラム(Ⅰ)~
学習目標


イベント・ドリブンプログラミングの利点を説
明できる
Swingを利用して簡単なGUIプログラムが
書ける



簡単なカスタムウインドウを表示できる
イベントハンドラの生成・登録ができる
ModelとViewを分離する設計について議
論できる
GUIアプリケーション

今回はいよいよGraphical User Interface
に挑戦します
購入画面
管理画面
14.1 SwingAPIを使う


14.1.1 Swing概要
14.1.2 Swingを使ってみよう
14.1.1 Swing概要

JavaでGUIのプログラムを書くためのAPI




GUIを一から作るのは大変
GUIに関するクラス・ライブラリ群は「Swing」と
呼ばれている
①ウインドウを出す
②カスタムウインドウを作る
Swing概要

すべてがオブジェクト

JLabel
「コンポーネント」という
JFrame
JTextField
JButton
14.1.2 Swingを使ってみよう

①ウインドウを出す
例題14-1
(Example14_1.java)
import javax.swing.*;//swingクラスライブラリの利用を宣言する
import java.awt.*;//swingクラスライブラリの利用を宣言する
//ウインドウを表示する
public class Example14_1 {
//プログラム・メイン
//フレームを起動する
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);//表示する
}
}
実行結果
次はこのウインドウにボタンを
乗せてみましょう!
②カスタムウインドウを作る

ウインドウにボタンを乗せる

JFrameを継承して、カスタムウインドウを作り
ます
Swing
プログラム
表示する()
再描画()など
JFrame
ButtonFrame
ButtonFrameのリスト
import javax.swing.*;//swingクラスライブラリの利用を宣言する
import java.awt.*;//swingクラスライブラリの利用を宣言する
例題14-2
(ButtonFrame.java)
//JFrameクラスを継承したカスタムフレーム(ウインドウ)クラス
//ボタンが一つ乗せられている
public class ButtonFrame extends JFrame{
//コンストラクタ
public ButtonFrame(){
getContentPane().setLayout(null);//ウインドウに載せられるすべてのオブジェクトの位置
を自分で設定できるようにする。
//ボタンを設定する
JButton button = new JButton();//ボタンをインスタンス化
button.setText("初めてのボタン");//ボタンのラベル名設定
button.setSize(150,20);//ボタンの大きさ設定
button.setLocation(20,50);//ボタンの位置設定(ウインドウからの相対位置)
//ボタンをウインドウに乗せる
getContentPane().add(button);
}
}
ButtonFrameを表示するMain
//ウインドウにボタンを乗せる
//カスタムフレーム(ウインドウ)の作成
public class Example14_2 {
例題14-2
(Example14_2.java)
//プログラム・メイン
//フレームを起動する
public static void main(String args[]){
JFrame frame = new ButtonFrame();//カスタムフレームオブジェクト生成
frame.setTitle("初めてのウインドウ");//タイトル設定
frame.setSize(200,200);//大きさ設定
frame.setLocation(50,50);//位置設定
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);//ウインドウが閉じたときに
プログラムが終了するように設定
frame.setVisible(true);//表示する
}
}
実行結果
ボタンが乗せられました。
しかし、押しても何も起こりません。
次は、ボタンが押されたとき、プログラムを
終了するようにしましょう!
14.2
イベント・ドリブンプログラミング


14.2.1 ボタンが押された時の処理を考え
る
14.2.2 Javaによるイベント・ドリブンプログ
ラミング
14.2.1 ボタンが
押された時の処理を考える

①原始的な方法

ポーリング

一定期間ごとにハードウエアの状態を調べる
ハードウエアの状態を調べる
何処にいる?
プログラム
動いてないよ!
押されてる?
プログラム
“a”が押されてます
ハードウエアを調べるプログラム

下記のメソッドを一定期間ごとに動かす
//Javaではない仮想言語
public void ボタンが押されたかどうかを調べて、押されていたら終了する
(){
マウスの位置を調べる();
if(マウスの位置がButtonの中にある){
マウスの状態を調べる();
if(マウスが押されている){
if(押されているのは、マウスの左ボタンである){
プログラムを終了する
}
}
}
}
}
ポーリングの問題点

考えてみよう
ポーリングの問題点

効率が悪い(CPUに負担をかける)



動いていないかもしれないのに調べる必要が
ある
マウスの正確な動きを捉えるには、相当細か
い期間ごとに調べなければならない
プログラミングが大変

ややこしいメソッドを用意する必要がある

ボタンがたくさんあったら大変
②イベント・ドリブン
プログラミング

イベント・ドリブン


ポーリングの問題点を解決するためのプログ
ラミングスタイル
処理をイベント毎に分割して記述して、 必要
に応じて呼びだされ, 処理される
イベント・ドリブンの考え方
押されたよ!
ハードウエアを
常に見張っている
唯一のプログラム
常に見張っている
プログラム
押されたよ!
プログラム
イベント・ドリブンの用語
イベント・ディスパッチャー
イベント
ハードウエアを
常に見張っている
唯一のプログラム
押されたよ!
イベント・ハンドラ
プログラム
イベント・ドリブンプログラミング

イベントドリブン型プログラムを作る時は、
ただひたすら、イベントハンドラをどう書く
かに集中すれば良い
public void ボタンが押されたときの処理(){
System.exit(0);
}
これだけのメソッドを書けばよくなる
14.2.2 Javaによる
イベント・ドリブンプログラミング




①Javaによるイベント・ドリブン
②イベントハンドラの作成
③イベントハンドラの登録
Javaテクニック


④インナークラス
⑤テキストフィールドと連動するプログラム
①Javaによるイベント・ドリブン



JavaのSwingフレームワークはこの機能を
標準で持っている
Javaではイベントハンドラのことを
EventListenerと呼び、オブジェクトとして表
現
Javaではイベントもオブジェクトとして扱う
(EventObject)
Javaによるイベント・ドリブン(2)
イベント・ディスパッチャー
Swingが提供
ハードウエアを
常に見張っている
唯一のプログラム
イベント
イベント・ハンドラ
EventObject
押されたよ
EventListener
プログラム
②イベントハンドラの作成
例題14-3
(ExitButtonListener.java)
import java.awt.event.*;//eventクラスライブラリの利用を宣言する
//ボタンを押したときのイベントハンドラ クラス
public class ExitButtonListener implements ActionListener{
//ボタンが押されたときのイベントハンドラ
public void actionPerformed(ActionEvent e){
ActionListenerはEventListenerの
サブインターフェイス
System.exit(0);//プログラムを終了する
}
}
メソッド名はインターフェイスが
決めているので間違えないように
ActionEventはEventObjectの
サブクラスで、イベントを表すクラス
③イベントハンドラの登録
//JFrameクラスを継承したカスタムフレーム(ウインドウ)クラス
public class ButtonFrame extends JFrame{
//コンストラクタ
public ButtonFrame(){
getContentPane().setLayout(null);
//ボタンを設定する
JButton button = new JButton();
button.setText("初めてのボタン");
button.setSize(150,20);
button.setLocation(20,50);
例題14-3
(ButtonFrame.java)
イベントハンドラを生成し、
addActionListener()メソッドを使って
登録する
//イベントハンドラを設定する
ExitButtonListener listener = new ExitButtonListener();//イベントハンドラをインスタンス化
button.addActionListener(listener);//イベントハンドラを受信者として登録する
//ボタンをウインドウに乗せる
getContentPane().add(button);
}
}
実行してみましょう!

ボタンを押すと、ウインドウが
閉じて終了するはずです
(Javaテクニック)
④インナークラス
//JFrameクラスを継承したカスタムフレーム(ウインドウ)クラス
public class ButtonFrame extends JFrame{
//コンストラクタ
public ButtonFrame(){
例題14-4
(ButtonFrame.java)
//ボタンを設定する
//途中割愛
//イベントハンドラを設定する
ExitButtonListener listener = new ExitButtonListener();//イベントハンドラをインスタンス化
button.addActionListener(listener);//イベントハンドラを受信者として登録する
//ボタンをウインドウに乗せる
getContentPane().add(button);
}
//ボタンを押したときのイベントハンドラ インナークラス
class ExitButtonListener implements ActionListener{
//ボタンが押されたときのイベントハンドラ
public void actionPerformed(ActionEvent e){
System.exit(0);//プログラムを終了する
}
}
}
インナークラス

クラスの中にクラスを書くことができる


Javaの便利な仕組み
インナークラスを使う利点は何でしょうか?
⑤テキストフィールド
と連動するプログラム

ボタンが押された時、テキストフィールドの
内容を変更するプログラム
ボタンが押された
テキストフィールド
と連動するプログラムのリスト
//JFrameクラスを継承したカスタムフレーム(ウインドウ)クラス
public class ButtonFrame extends JFrame{
private JTextField textField = new JTextField();//テキストフィールド
テキストフィールドを
生成する
//コンストラクタ
public ButtonFrame(){
//ボタンを設定する(割愛)
//イベントハンドラを設定する(割愛)
//ボタンをウインドウに乗せる(割愛)
//テキストフィールドを設定する
textField.setText("ボタンは押されていません");
textField.setBounds(20,100,150,20);//位置、大きさ設定(x,y,width,height)
getContentPane().add(textField);//テキストフィールドをウインドウに載せる
}
//ボタンを押したときのイベントハンドラ インナークラス
public class TextFieldChangeButtonListener implements ActionListener{
//ボタンが押されたときのイベントハンドラ
public void actionPerformed(ActionEvent e){
textField.setText("ボタンが押されました");
}
}
}
テキストフィールドの
内容を変更する
インナークラスの利点

簡単に他のコンポーネントと連動できるよ
うになる

もちろん、イベントハンドラに参照を渡すように
して、プログラムを分割してもできる

プログラムを分割する必要がある場合はそうする
14.3 GUI自動販売機の構成



14.3.1 GUI自動販売機概要
14.3.2 ModelとViewの分離
14.3.3 GUI自動販売機プログラミング
14.3.1 GUI自動販売機概要


自動販売機用コンポーネントを利用する
基本構造

管理者用ウインドウ(AdminFrameクラス)


CUI版CUIAdminAppの機能を備えている
ユーザ用ウインドウ(UserFrameクラス)

CUI版CUIUserAppの機能を備えている
基本構造
ユーザウインドウ
管理者ウインドウ
14.3.2 ModelとViewの分離




①CUI自動販売機の再利用
②ModelとViewの分離
③現状のModelの問題点
④display()メソッドの廃止
①CUI自動販売機の再利用

CUIアプリケーションのオブジェクトは再利
用できるものがある
勘定
金
- 価値
CUIUserApp
+ 追加()
+ 削除()
商品種類
- 名前
- 商品番号
0..n
- 価格
商品種類リスト
+ 追加()
1 + 削除()
商品保管庫
商品
- 製造年月日
0..n
CUIAdminApp
+ 追加()
1 + 削除()
再利用可能
GUI自動販売機の構成

GUIになっても構造は変わらない
勘定
金
- 価値
商品種類
- 名前
- 商品番号
- 価格
AdminFrame
+ 追加()
+ 削除()
商品種類リスト
0..n
+ 追加()
1 + 削除()
商品保管庫
商品
- 製造年月日
0..n
+ 追加()
1 + 削除()
UserFrame
②ModelとViewの分離

再利用できるものとできないものの違いを
考える
ModelとView

再利用できるもの


自動販売機における実体や概念の構造に関
わる部分→Model
再利用できないもの


表示に関する部分
→View
(アプリケーションロジックに関わる部分)
ModelとViewの分離

ModelとViewを分離しておくことによって、
表示の変更が容易になる

分離していなかったらGUIは始めから作り直し
実体や概念
Viewクラス群
UserFrame
表示役
+ 追加()
+ 削除()
Modelクラス群
商品種類
- 名前
- 商品番号
- 価格
AdminFrame
勘定
金
- 価値
商品種類リスト
0..n
+ 追加()
1 + 削除()
商品保管庫
商品
- 製造年月日
0..n
+ 追加()
1 + 削除()
③現状のModelの問題点

ItemTypeListクラスのdisplay()メソッドは、
CUI専用のコードになっている
例題11-1
(ItemTypeList.java)
#display()
//商品種類を表示する
public void display(){
for(int i=0;i<size;i++){
//商品種類を表示
System.out.println(itemTypeArray[i].getId()+":"+itemTypeArray[i].getName()+":"
+itemTypeArray[i].getPrice()+"円");
}
}
※ItemStockクラスも同様
display()メソッド

CUIとGUIでは異なる表示にしたい


現状のdisplay()メソッドではGUIに対応できな
い
Modelは構造だけに責任をもつ
1001:cola:120円
1002:soda:120円
CUI商品種類表示
GUI商品種類表示
④display()メソッドの廃止


表示の責任はViewが持つべきだから、
display()メソッドは廃止する
変わりにCUI、GUIがそれぞれの表示をで
きるようにメソッドを追加する

int size()


要素数を取得する
ItemType get(int index)

index番目の商品種類を取得する
display()はViewに任せる
//商品種類リストを閲覧する
public void showItemTypeList(){
itemTypeList.display();
}
//商品種類リストを閲覧する
public void showItemTypeList(){
int len = itemTypeList.size();
for(int i=0;i<len;i++){
ItemType itemType = itemTypeList.get(i);
System.out.println(itemType.getId()+":"
+itemType.getName()+":"
+itemType.getPrice()+"円");
}
}
例題11-2
(CUIAdminApp.java)
# showItemTypeList()
size()メソッドと
get(index)メソッド
があれば、表示可能
Model-View分離

ModelとViewで責任を分離したプログラム
の利点


表示の変更が容易に行える
様々な表示(CUI,GUI)に対応したModelプロ
グラムが書ける

重複コードがなくなる
14.3.3
GUI自動販売機プログラミング




①自動販売機用コンポーネント
②GUI自動販売機の起動
③UserFrame解説
④100円投入ボタンを追加する
①
自動販売機用コンポーネント(1)

代表的なクラス
BackgroundPanelクラス
背景用のコンポーネントです。
この上に他の自販機コンポーネントを乗せる
ことができます。
ItemImagePanelクラス
商品種類を表示するための
コンポーネントです。
①
自動販売機用コンポーネント(2)
BuyButtonクラス
購入ボタンです
ProductOutletクラス
商品取り出し口です
CoinDepositクラス
投入金額を表示します
②GUI自動販売機の起動
例題14-6
(Example14_6.java)
//GUI自動販売機を起動するためのメイン・クラス
public class Example14_6 {
/**
* プログラム・メイン
* 操作するための2つのウインドウ(管理ウインドウ,購入ウインドウ)
* を生成、表示する
*/
public static void main(String args[]){
ItemTypeList itemTypeList = new ItemTypeList();
//商品種類リストを生成
Account account = new Account();
//投入金勘定を生成
//管理ウインドウを生成して表示する
AdminFrame adminFrame = new AdminFrame(itemTypeList,account);//生成
adminFrame.setVisible(true);//表示
itemTypeListと
Accountを生成して
2つのウインドウに渡している
//購入ウインドウを生成して表示する
UserFrame userFrame = new UserFrame(itemTypeList,account);//生成
userFrame.setVisible(true);//表示
}
③UserFrame解説

コンストラクタ


各種コンポーネントを生成して、ウインドウに
貼り付ける
更新メソッド(3種類)


例題14-6
(UserFeame.java)
Modelの状態を反映して、表示を更新するた
めに使う
イベント・ハンドラ

ボタンが押された時のイベントハンドラを記述
④100円投入ボタンを追加する

100円投入できるようにする
ボタンが押されたら
100円投入ボタンを追加する
//ユーザフレーム(商品購入メインウインドウ)クラス
//説明に不要な部分を割愛してある
public class UserFrame extends JFrame{
public JButton button100 = new JButton();//100円ボタン
例題14-7
(UserFeame.java)
ボタン生成
//コンストラクタ
public UserFrame(ItemTypeList newItemTypeList,Account newAccount){
//100円投入ボタン設定
button100.setText("100円");
button100.setBounds(new Rectangle(385, 240, 79, 27));
button100.addActionListener(new Button100_ActionListener());
background.add(button100, null);
ボタン設定
}
//100円投入ボタン・イベントハンドラ
class Button100_ActionListener implements java.awt.event.ActionListener{
public void actionPerformed(ActionEvent e) {
account.insert(new Money(100));//100円投入する
stateUpdate();//状態を更新する
}
}
}
イベント・ハンドラ
イベント・ハンドラ解説

投入金勘定への金の追加


CUIと同様
stateUpdate()を呼ぶ

構造が変化したらstateUpdate()を呼ぶと表示が更新
される
//100円投入ボタン・イベントハンドラ
class Button100_ActionListener implements java.awt.event.ActionListener{
public void actionPerformed(ActionEvent e) {
account.insert(new Money(100));//100円投入する
stateUpdate();//状態を更新する
}
}
課題ヒント

課題14-3:購入ボタンのイベント・ハンドラ
を実装せよ
//ユーザフレーム(商品購入メインウインドウ)クラス
//購入ボタンのイベントハンドラ以外は割愛
public class ShoppingFrame extends JFrame{
//購入ボタン・イベントハンドラ
//押されたボタンと、そのボタンが対象とする商品の種類が引数
public void buyButton_pressed(BuyButton buyButton,ItemType itemType){
}
}
購入ボタン用のイベントハンドラが用意されている
購入イベント・ハンドラの実装

プログラムの目的(コメント)は、CUIプログラムと同様になる

コメントに合わせてプログラムを書く
//購入ボタン・イベントハンドラ
//押されたボタンと、そのボタンが対象とする商品の種類が引数
public void buyButton_pressed(BuyButton buyButton,ItemType itemType){
//買えるかどうか確認する
//在庫の確認をする
//投入されている金が価格より高いか確認をする
//購入する
//保管庫から商品を取り出す
productOutlet.addItem(item); //取り出し口に出す
取り出し口に出すプログラム
itemは取り出した商品
//おつりをだす
System.out.println("おつりは~~円です"); //おつりの表示
//投入金勘定をリセットする
stateUpdate();//状態を更新する
}
構造が変化したら、状態を更新する