page10(update:2016/12/12)


[ prev | next | index ]

10. GUI(Graphical User Interface)

ここではGUIを実現する為のクラスライブラリについて説明する。これらのクラスは単体では機能せず、パッケージにまとめられた多数のクラスが役割を分担し、協力することでGUIを実現している。まずは役割分担の概要を理解することが必要だ。

目次

[目次]

10.1 GUI関連クラスライブラリの変遷 

GUIを利用したプログラムを書く為に用意されたクラスのライブラリはjavaのバージョンアップとともに変遷してきた。
  1. jdk 1.0.2 java.awtパッケージにGUI部品クラスを用意
  2. jdk 1.1 イベントモデルの変更
  3. jdk 1.2〜1.4 swingの追加。 java 2
  4. jdk 1.5 Generics 列挙型など文法に大幅な追加があり java 5に名称変更
  5. jdk1.6〜現在 Java 8 (jdk1.8)

ここでGUIについてはjdk 1.1レベルの説明を行い、swingについては例題に留める
これはjdk 1.0.2レベルの仕様は推奨されないモノになったこととjdk 1.1のイベントモデルはswingでも同様に使われるためである。jdk1.2からのswingは高機能な反面で複雑なため本質が見えにくい。jdk 1.1レベルが理解できればswingは自習可能なので、ここでは例題での利用にとどめました。

※javaFXはAWTとは別系統で作られているので、ここでは解説しません。
javaFXのAPIドキュメント

[目次]

10.2 AWTクラスの役割分担

 AWT(Abstract Window Toolkit)はjava.awt.パッケージに収められている。ここで、 AWTは多数のクラスを使ってGUIの機能を実現しているが、下の図のように、土台となるクラスやインターフェイスで大まかな役割を決め、これらを継承や 実装して具体的な部品クラスを作っている。

※ AWT(Abstract Window Toolkit)の Abstractは抽象的なものの意味で、OSなどの実行環境に依存しないことを強調している。

クラスの関係


  1. Component クラス 
    画面に表示される部品として必要なフィールドやメソッドを用意する役割を持つ。
    フィールド:表示する部品の位置と大きさなど
    メソッド:描画で呼び出されるメソッド郡、描画更新要求の受け取りメソッドなど多数
    自分に描画するためのGraphicsオブジェクトを持っている
    Componentを継承した具体的なクラスとしてButtonやLabelなどがある。
     
  2. Container クラス 
    表示部品を格納し、まとめる役割を持つコンテナ。Componentを継承することでコンテナにコンテナを入れる入れ子構造が可能になって いる。 Containerを継承した具体的なクラスとしてFrameやPanelなどがある。

    格納した部品を表示するときの配置(レイアウト)を自動調整するLayoutManagerを持っている。このLayoutManagerは取り替えることも、外すことも可能。

SWING のJButtonやJPanelクラスは上記のContainerクラスを継承したJComponentクラスの下に作られている。しかしJFameなどはFrameを直接継承しているなど統一性に欠けた部分も見られます。

イベント・モデル

オブジェクトが連携して動作するためにはオブジェクト間でデータの受け渡しが必要になる。簡単な内容ならメソッドの引数に情報をコピーして渡せばいいが、渡す情報の種類が増えて複雑になると、引数の数が増えていずれは破綻する。

※マウスのクリック情報を基本データ型の引数で関数に渡そうとすると、クリックされた位置座標、右クリックか左クリックか、シフトキー等のオプションキーは押されていたのか、マウスのボタンは何個か、ルーラーは在るのか、などなどで情報には切りがない。

そこで情報をまとめて保持したオブジェクトを生成して、この参照を引数で渡す形が使われる。オブジェクトは継承による多態性を利用して多様な情報にも対応できる。また、可視性を利用してデータを勝手に書き換えたりはできないように制限も可能で情報を受け渡すうえでオブジェクトを使うのは優れた方法と言える。

※沢山のオブジェクトに同じ情報を配信する場合、イベントオブジェクトを1個作って、その参照を配れば無駄なメモリーを使わないし、効率もよくなる。

javaのAWTでは再描画が必要になったりマウスやキー入力などの何かのイベントが発生したときに、イベントオブジェクトを生成してイベントを処理するオブジェクトに配信する。

jdk1.0.2ではイベントオブジェクトの受け取り先が固定されていた。イベントの処理を行うときにはイベントを受け取るクラスを継承し、 イベント処理を独自のものに上書きしたクラスを作る必要があ った。Javaは単一継承なので、この条件はプログラムを作るうえで大きな制約になってしまう。

jdk1.1からはイベントを受け取るための登録を行えばイベントの配信を受けられる仕組みに変更された。

 イベントの発生源をイベントソース、イベントを受けとる側をイベントリスナと呼ぶ。

予めイベントソース側 にイベントリスナを登録しておく。イベントが発生するとイベントオブジェクトが生成され、登録されたイベントリスナにこのイベントオブジェクトが配信される。

下の図は、イベントを配信するイベントソース、配信されるイベント、受けとる側のインターフェイスの対応を示しています。受取に 必要なメソッドが多い場合、何もしないメソッドで実装したアダプタークラスが用意されています。

コンポーネントの移動およびサイズ変更はComponentEventによって伝達されるが、 AWTは内部で自動的にこれを扱う

[目次]

10.3 部品の組み立て

 表示される部品を一般化したクラスがComponentで、これを継 承してComponentを入れる箱クラスContainerが用意されてい る。箱の中の部品の配置はContainerに用意されたLayoutManagerに よって行われる。これらの3種類の部品を順に説明し、最後に例題を示す。

10.3.1 Component

直接に画面に描かれる部品をあらわす抽象クラス。 画面上に描画する外形の設定や取得のメソッド、描画のためのメソッドなどを持つ。これまでに使ったButtonやLabelなどの具体的なクラスはこれを継承して作られている。

自分に描画するためのGraphicsオブジェクトを持っている。Graphicsについては次の10.3.2で説明する。

外形の設定や取得のメソッド

部品は矩形で左上隅の位置座標と、幅と高さを持っている。これらのデータの取得や設定は以下のようなメソッドで行える。

描画のためのメソッド

この他にも描画される部品の一般的な特性、表示のON/OFF、色、使われる文字フォントなどがgetXX()やsetXX()のようなメソッ ドで取得、変更できる。 

Componentを継承した主なクラス

Componentは抽象クラスなので、インスタンスを作れません。実際に画面に表示されるのはComponentを継承した具体的なクラスで す。以下の太字で示したモノは重要です。この他にメニューを作るための部品も有りますが、これらはAWTではMenuComponentを継承した別の系統になって います。しかし、 SWINGではComponentを継承したクラスとして作られたJMenuなどが用意され、統一性のある継承階層になっています。

※SWINGとAWTの具体的描画部品を混ぜて使うことはできない。連携がうまく取れ無いことが出てくる。

10.3.2 Graphics

Componentに描画を行うクラス。Componentインスタンスから渡されるGraphicsインスタンスは、そのComponentに描画を行う。

※Graphicsとして渡されるインスタンスは、実はGraphicsを継承したGraphics2Dのインスタンス。
そこで、Graphics2Dにキャストすれば、より高度な描画メソッドが利用可能になる。線の太さやスタイル、拡大縮小と回転などのアフィン変換も可能になっている。

10.3.3 Container

Componentを格納するクラス。ContainerはComponent を継承している為にContainerにContainerを入れることが可能です。

コンテナに格納した中身の部品はLayoutManagerにより適切に配置されます。LayoutManagerはContainerの setLayout(LayoutManager mgr) メソッドで交換可能です。 LayoutManagerについては次に述べます。

Containerを継承したAWTのクラスには以下のものがあります

10.3.4 LayoutManager

パネルやウインドウの大きさが変化した時に中身の部品を自動的に再配置する。標準でウインドウやパネルに付いて来ますが付け替 えも可能です。例えばsetLayout(new CardLayout())でLayoutManagerをCardLayoutに変更できます。もし自動レイアウトをやめたければsetLayout (null)でLayoutManagerを外せます。

インターフェイスLayoutManagerを実装した主なクラスには以下のものがあります。

10.3.5 例題10a 

GridLayout、FlowLayout、BorderLayoutの3種類のレイアウトをCardLayoutで順番に見 せる例題。

以下のプログラムを実行しボタンを押してLayoutを変更し、さらにウインドウの大きさを変えるなどしてレイアウトを確かめて ください。

/*MyFrame4.java*/
import java.awt.*;
import java.awt.event.*;


public class MyFrame4 extends Frame //Layout確認用サンプル
{
static public void main(String args[])
{
Frame window=new MyFrame4();
window.setSize(300,200);
window.setVisible(true);
}
//
Panel centerPanel;
CardLayout card;
public MyFrame4()
{
super("MyFrame4");
//centerPanelの作成
centerPanel=new Panel();
card=new CardLayout();
centerPanel.setLayout(card);

Panel panel;

//GridLayoutのパネル作成
panel=new Panel();
panel.setLayout(new GridLayout(3,2));
panel.add(new Button("1"));
panel.add(new Button("2"));
panel.add(new Button("3"));
panel.add(new Button("4"));
panel.add(new Button("5"));
panel.add(new Button("GridLayout"));
//これをCardLayoutのcenterPanelに貼り付け
centerPanel.add("GridLayout(3,2)", panel);

//FlowLayoutのパネル作成
panel=new Panel();
panel.setLayout(new FlowLayout());
panel.add(new Button("1"));
panel.add(new Button("2"));
panel.add(new Button("3"));
panel.add(new Button("4"));
panel.add(new Button("5"));
panel.add(new Button("FlowLayout"));
//これをCardLayoutのcenterPanelに貼り付け
centerPanel.add("FlowLayout()", panel);

//BorderLayoutのパネル作成
panel=new Panel();
panel.setLayout(new BorderLayout());
panel.add(new Button("BorderLayout中央"),BorderLayout.CENTER);
panel.add(new Button("東"),BorderLayout.EAST);
panel.add(new Button("南"),BorderLayout.SOUTH);
panel.add(new Button("西"),BorderLayout.WEST);
panel.add(new Button("北"),BorderLayout.NORTH);
//これをCardLayoutのcenterPanelに貼り付け
centerPanel.add("BorderLayout()", panel);

//centerPanelをFrameの中央に貼り付け
add(centerPanel,BorderLayout.CENTER);
//cardをめくるボタンをFrameの南に貼り付け
Button button=new Button("次のLayout");
button.setBackground(Color.yellow);
add(button,BorderLayout.SOUTH);

//イベント処理の設定
Control control=new Control();
addWindowListener(control);
button.addActionListener(control);

}

/*内部クラス ウインドウを閉じる操作とプログラムの終了を代行する*/
class Control extends WindowAdapter implements ActionListener
{
//ここでは依頼者MyFrameのメンバを自由に呼べる

public void windowClosed(WindowEvent e)
{
System.exit(0);//プログラムの終了
}
public void windowClosing(WindowEvent e)
{
dispose();//依頼者のメソッドdisposeを呼ぶ
//disposeの結果windowClosedが呼ばれる
}
public void actionPerformed(ActionEvent e)
{
card.next(centerPanel);
}
}

}

[目次]

10.4 SWING

SWINGのクラスはjavax.swingパッケージにまとめられています。AWTにくらべて豊富な部品が用意されています。

AWTで紹介したComponentContainer、LayoutManagerを継承したり、実装したりした多くのクラスラが用意されていますが、従来のAWTの部品と交ぜて使うと、旨く動きません。SWINGではAWTのFrame,Buttonなどの部品を置き換える部品が用意されています。この様な部品は対応が分かるようにAWTのクラス名の先頭にJを追加した名前になっています。例えばJFrame,JButtonなどです。(ただし、JCanvasは無い)

javax.swing.JFrame

前に一文字Jが付くだけですが、Frameに比べると構造は複雑です。例えば、Frameの表示面は一層でAWTの部品はその上 に載せましたが、JFrameは複数の表示レイヤーを持っています。このため、JButtonなどの部品をJFrameに貼るときは、JFrameからgetContentPain ()で取得したコンテナに部品を入れる必要があります。

※pane(複数形panes)窓枠、窓ガラス、平面、碁盤の目状の区画。 JFrameは複数のpaneを持っています。

以下にプログラム例を示す。SWINGではHTMLに対応するなど様々な機能が追加されている。

例題10b

  1. JFrameからコンテナを取り出して、そこに部品を置く
  2. JLabelやJButtonの表示ではHTMLが利用可能
  3. JFrameはウインドウを閉じるボタンからのイベント処理を簡単に設定可能
  4. JButtonからのイベントの処理方法はAWTと同様に書ける

/*MyFrame5.java*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MyFrame5 extends JFrame implements ActionListener
{
static public void main(String args[])
{
Frame window=new MyFrame5();
window.setSize(300,200);
window.setVisible(true);
}
//

public MyFrame5()
{
super("MyFrame5");
Container container=getContentPane();//コンテナを取り出して部品を入れる必要がある //SWINGの部品ではHTMLが利用できる。ボタンやラベルにHTMLで絵や文字の表示を指示できる String html="<HTML><BODY><H1>JLabel</H1><IMG SRC=\"http://www.ics.kagoshima-u.ac.jp/~mizuno/welcom.gif\"></BODY></HTML>"; JLabel label=new JLabel(html); container.add(label,BorderLayout.CENTER);//取り出したコンテナに部品を入れる // String html2="<HTML><BODY><IMG SRC=\"http://www.ics.kagoshima-u.ac.jp/~mizuno/blue.gif\">JButton</BODY></HTML>"; JButton button=new JButton(html2); container.add(button,BorderLayout.SOUTH);//取り出したコンテナに部品を入れる //イベント処理の設定 setDefaultCloseOperation(EXIT_ON_CLOSE);//×ボタンでexitを使用してプログラムを終了する設定 button.addActionListener(this);//ボタンにイベント送信を予約 } public void actionPerformed(ActionEvent e) { System.out.println("ボタンが押されました"); } }

10.5 図形エディタのいいかげんなプロトタイプを作ってみた(リンク) 


[ prev | next | index ]