ここではGUIを実現する為のクラスライブラリについて説明する。これらのクラスは単体では機能せず、パッケージにまとめられた多数のクラスが役割を分担し、協力することでGUIを実現している。まずは役割分担の概要を理解することが必要だ。
[目次]
GUIを利用したプログラムを書く為に用意されたクラスのライブラリはjavaのバージョンアップとともに変遷してきた。
※ここでGUIについてはjdk 1.1レベルの説明を行い、swingについては例題に留める。
これはjdk
1.0.2レベルの仕様は推奨されないモノになったこととjdk
1.1のイベントモデルはswingでも同様に使われるためである。jdk1.2からのswingは高機能な反面で複雑なため本質が見えにくい。jdk
1.1レベルが理解できればswingは自習可能なので、ここでは例題での利用にとどめました。
※javaFXはAWTとは別系統で作られているので、ここでは解説しません。
javaFXのAPIドキュメント
[目次]
AWT(Abstract Window Toolkit)はjava.awt.パッケージに収められている。ここで、 AWTは多数のクラスを使ってGUIの機能を実現しているが、下の図のように、土台となるクラスやインターフェイスで大まかな役割を決め、これらを継承や 実装して具体的な部品クラスを作っている。
※ AWT(Abstract Window Toolkit)の Abstractは抽象的なものの意味で、OSなどの実行環境に依存しないことを強調している。
※SWING のJButtonやJPanelクラスは上記のContainerクラスを継承したJComponentクラスの下に作られている。しかしJFameなどはFrameを直接継承しているなど統一性に欠けた部分も見られます。
オブジェクトが連携して動作するためにはオブジェクト間でデータの受け渡しが必要になる。簡単な内容ならメソッドの引数に情報をコピーして渡せばいいが、渡す情報の種類が増えて複雑になると、引数の数が増えていずれは破綻する。
※マウスのクリック情報を基本データ型の引数で関数に渡そうとすると、クリックされた位置座標、右クリックか左クリックか、シフトキー等のオプションキーは押されていたのか、マウスのボタンは何個か、ルーラーは在るのか、などなどで情報には切りがない。
そこで情報をまとめて保持したオブジェクトを生成して、この参照を引数で渡す形が使われる。オブジェクトは継承による多態性を利用して多様な情報にも対応できる。また、可視性を利用してデータを勝手に書き換えたりはできないように制限も可能で情報を受け渡すうえでオブジェクトを使うのは優れた方法と言える。
※沢山のオブジェクトに同じ情報を配信する場合、イベントオブジェクトを1個作って、その参照を配れば無駄なメモリーを使わないし、効率もよくなる。
javaのAWTでは再描画が必要になったりマウスやキー入力などの何かのイベントが発生したときに、イベントオブジェクトを生成してイベントを処理するオブジェクトに配信する。
jdk1.0.2ではイベントオブジェクトの受け取り先が固定されていた。イベントの処理を行うときにはイベントを受け取るクラスを継承し、 イベント処理を独自のものに上書きしたクラスを作る必要があ った。Javaは単一継承なので、この条件はプログラムを作るうえで大きな制約になってしまう。
jdk1.1からはイベントを受け取るための登録を行えばイベントの配信を受けられる仕組みに変更された。
イベントの発生源をイベントソース、イベントを受けとる側をイベントリスナと呼ぶ。
予めイベントソース側 にイベントリスナを登録しておく。イベントが発生するとイベントオブジェクトが生成され、登録されたイベントリスナにこのイベントオブジェクトが配信される。
下の図は、イベントを配信するイベントソース、配信されるイベント、受けとる側のインターフェイスの対応を示しています。受取に 必要なメソッドが多い場合、何もしないメソッドで実装したアダプタークラスが用意されています。
※
コンポーネントの移動およびサイズ変更はComponentEventによって伝達されるが、 AWTは内部で自動的にこれを扱う。
[目次]
表示される部品を一般化したクラスがComponentで、これを継 承してComponentを入れる箱クラスContainerが用意されてい る。箱の中の部品の配置はContainerに用意されたLayoutManagerに よって行われる。これらの3種類の部品を順に説明し、最後に例題を示す。
直接に画面に描かれる部品をあらわす抽象クラス。 画面上に描画する外形の設定や取得のメソッド、描画のためのメソッドなどを持つ。これまでに使ったButtonやLabelなどの具体的なクラスはこれを継承して作られている。
自分に描画するためのGraphicsオブジェクトを持っている。Graphicsについては次の10.3.2で説明する。
部品は矩形で左上隅の位置座標と、幅と高さを持っている。これらのデータの取得や設定は以下のようなメソッドで行える。
- 位置
- int getX() 原点の現在の x 座標を返します。
- int getY() 原点の現在の y 座標を返します。
- Point getLocation() 位置座標をPoint(点)オブジェクトとして返します。
- Point getLocation(Point rv) 座標を「戻り値」rv に格納し、rv を返します。 新たなインスタンスを生成しな分効率的。
- void setLocation(int x, int y) 部品を新しい位置に移動します。
- void setLocation(Point p) 部品を新しい位置に移動します。
- 幅と高さ
- int getWidth() この部品の現在の幅を返します。
- int getHeight() この部品の現在の高さを返します。
- Dimension getSize() このコンポーネントのサイズを Dimension オブジェクトとして返します。
- Dimension getSize(Dimension rv) rvにサイズを入れる。新たなインスタンスを生成しない分効率的。
- void setSize(int width, int height) 部品のサイズを、幅 width、高さ height に変更します。
- void setSize(Dimension d) 部品のサイズを変更します。
- 位置と大きさを一度に
- Rectangle getBounds() この部品の境界を Rectangle オブジェクトとして返します。
- Rectangle getBounds(Rectangle rv) rvにサイズを入れる。新たなインスタンスを生成しない分効率的。
- void setBounds(int x, int y, int width, int height) この部品を移動し、サイズ変更します。
- void setBounds(Rectangle r)
この他にも描画される部品の一般的な特性、表示のON/OFF、色、使われる文字フォントなどがgetXX()やsetXX()のようなメソッ ドで取得、変更できる。
- 描画(ペイント)
- void repaint() このコンポーネントを再描画するときに呼ばれる。
- void update(Graphics g) repaintの結果として呼び出され、このコンポーネントを更新する。
背景を描画した後paintを呼び出す。- void paint(Graphics g) このコンポーネントを描 画する。
Graphicsについては次の10.3.2で説明するが、引数で渡されたGraphicsを用いて描画を行う。- 表示のON/OFF
- void setVisible(boolean b) b がtrueで表示、falseで非表示にします。
- 色
- Color getForeground() フォアグラウンドカラーを返す
- Color getBackground() バックグラウンドカラー(背景色)を返す
- void setForeground(Color c) フォアグラウンドカラーを設定
- void setBackground(Color c) バックグラウンドカラー(背景色)を設定
- 文字フォント
- Font getFont() フォントを返します。
- setFont(Font f) フォントを設定
- ...............その他にも多くのメソッドがあって書ききれない...............
Componentは抽象クラスなので、インスタンスを作れません。実際に画面に表示されるのはComponentを継承した具体的なクラスで す。以下の太字で示したモノは重要です。この他にメニューを作るための部品も有りますが、これらはAWTではMenuComponentを継承した別の系統になって います。しかし、 SWINGではComponentを継承したクラスとして作られたJMenuなどが用意され、統一性のある継承階層になっています。
※SWINGとAWTの具体的描画部品を混ぜて使うことはできない。連携がうまく取れ無いことが出てくる。
- Component
- Button(ボタン)
- Canvas(矩形の画面領域)
- Checkbox
- Choice
- Container(Componentを格納する箱、次で説明)
- Window
- Frame
- JFrame(SWINGの部品)
- JComponent(SWINGの部品)
- AbstractButton(SWINGの部品)
- JButton(SWINGの部品)
- JLabel(SWINGの部品)
- JPanel(SWINGの部品)
- Label(編集できないテキスト)
- List
- Scrollbar
- TextComponent
- TextArea(編集可能な複数行のテキスト枠、スクロールバー付)
- TextField (編集可能な一行のテキスト枠)
Componentに描画を行うクラス。Componentインスタンスから渡されるGraphicsインスタンスは、そのComponentに描画を行う。
- 座標変換
- void translate(int x, int y) グラフィックスコンテキストの原点を現在の座標系の (x, y) に変換します。
- 画像の描画(下記は一例)
- boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
指定された矩形の内部に収まるようにスケーリングして、指定されたイメージの利用可能な部分を描きます。- 描画色の取得と設定
- Color getColor()このグラフィックスコンテキストの現在の色を返します。
- void setColor(Color c)このグラフィックスコンテキストの現在の色を、指定された色に設定します。
- 図形の描画
- void drawLine(int x1, int y1, int x2, int y2)
座標 (x1, y1) と座標 (x2, y2) との間に現在の色を使って線を描きます。- void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) 連続的に接続している直線を描きます。
- void drawRect(int x, int y, int width, int height)
矩形の輪郭を描きます。- void fillRect(int x, int y, int width, int height)
塗りつぶされた矩形を描きます。- void drawOval(int x, int y, int width, int height) 楕円の輪郭を描きます。
- void fillOval(int x, int y, int width, int height) 塗りつぶされた楕円形を描画
- void drawPolygon(Polygon p) 多角形の輪郭を描きます。
- void fillPolygon(Polygon p) 塗りつぶされた多角形を描画。
- 文字 の描画
- void drawString(String str, int x, int y)
文字列strをx,yの位置を左下端として描く。- Font getFont()現在のフォントを返します。
- void setFont(Font font) 指定されたフォントに設定します。
- −−−−−−−−−上記は一例ですまだ多くのメソッドがあります−−−−−−−−−−
※Graphicsとして渡されるインスタンスは、実はGraphicsを継承したGraphics2Dのインスタンス。
そこで、Graphics2Dにキャストすれば、より高度な描画メソッドが利用可能になる。線の太さやスタイル、拡大縮小と回転などのアフィン変換も可能になっている。
Componentを格納するクラス。ContainerはComponent を継承している為にContainerにContainerを入れることが可能です。
コンテナに格納した中身の部品はLayoutManagerにより適切に配置されます。LayoutManagerはContainerの setLayout(LayoutManager mgr) メソッドで交換可能です。 LayoutManagerについては次に述べます。
Containerを継承したAWTのクラスには以下のものがあります
パネルやウインドウの大きさが変化した時に中身の部品を自動的に再配置する。標準でウインドウやパネルに付いて来ますが付け替 えも可能です。例えばsetLayout(new CardLayout())でLayoutManagerをCardLayoutに変更できます。もし自動レイアウトをやめたければsetLayout (null)でLayoutManagerを外せます。
インターフェイスLayoutManagerを実装した主なクラスには以下のものがあります。
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);
}
}
}
[目次]
SWINGのクラスはjavax.swingパッケージにまとめられています。AWTにくらべて豊富な部品が用意されています。
AWTで紹介したComponent、Container、LayoutManagerを継承したり、実装したりした多くのクラスラが用意されていますが、従来のAWTの部品と交ぜて使うと、旨く動きません。SWINGではAWTのFrame,Buttonなどの部品を置き換える部品が用意されています。この様な部品は対応が分かるようにAWTのクラス名の先頭にJを追加した名前になっています。例えばJFrame,JButtonなどです。(ただし、JCanvasは無い)
前に一文字Jが付くだけですが、Frameに比べると構造は複雑です。例えば、Frameの表示面は一層でAWTの部品はその上 に載せましたが、JFrameは複数の表示レイヤーを持っています。このため、JButtonなどの部品をJFrameに貼るときは、JFrameからgetContentPain ()で取得したコンテナに部品を入れる必要があります。
※pane(複数形panes)窓枠、窓ガラス、平面、碁盤の目状の区画。 JFrameは複数のpaneを持っています。
以下にプログラム例を示す。SWINGではHTMLに対応するなど様々な機能が追加されている。
/*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+23-456/7*890= 答えは図のようになるはずです。×ボタンで終了しレポート送信
説明に合うようにAWTを使ったプログラムです。変更部分はファイルP9.javaの最後の部分で,ヒントも書いています