教育サーバーのページ
オンラインテキスト目次
ソフトウエア基礎演習

模範解答

GUIアプリケーション (AWT) その2

課題へ

グラフィックス描画

多くのGUI環境ではグラフィックスを表示することができる。 一般にグラフィックスとは、線や点、四角形などの図形のことを指す。

JavaのAWTでは、Graphicsオブジェクトを使ってグラフィックス描画ができる。
※ 現在では、Java2Dというさらに高級なグラフィックス描画の仕組みが用意されているが、ここでは従来からAWTに備わっている機能を紹介する。

以下のプログラムDrawGraphicsTest.javaはJavaによるグラフィックス描画の例である。

import java.awt.*;
import java.awt.event.*;

class GraphicsFrame extends Frame {
    public GraphicsFrame() {
	setSize(200, 200);
	addWindowListener(new MyWindowAdapter());
    }
    public void paint(Graphics g) {
	g.drawLine(50,50,150,150);
	g.drawLine(50,150,150,50);
	g.drawRect(70,70,60,60);
	g.drawOval(50,50,100,100);
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
	System.exit(0);
    }
}

public class GraphicsTest {
    public static void main(String[] args) {
	GraphicsFrame f = new GraphicsFrame();
	f.setVisible(true);
    }
}

プログラムの説明

Frameクラスから継承したGraphicsFrameクラスにpaintメソッドを定義している。 プログラムの他の部分は従来と変わらない。 paintメソッドはFrameなどのコンポーネントに何か描画すべきときに呼ばれるメソッドである。 Frame内に何か描画したければ、paintメソッド内にそのプログラムを書けば良いわけである。

paintメソッドにはGraphics型の引数があり、 このGraphicsオブジェクトが備えるグラフィックス描画のメソッドを呼べば良い。 例えば、

g.drawLine(50,50,150,150)

は、 Graphicsオブジェクトのgに対して直線を描画せよというdrawLineメソッドを呼び出している。 drawLineメソッドの引数をそれぞれx1, y1, x2, y2 とすると、 drawLineメソッドは、点(x1,y1)から点(x2,y2)までの直線を描画する。 つまり、例の場合は、(50,50)-(150,150)の直線を描画する。 次の、

g.drawLine(50,150,150,50)

は、 (50,150)-(150,50)の直線を描画する。この2本の線でちょうど×印を描いているのである。 なお、Graphicsオブジェクトを使う場合、原点(0,0)がコンポーネントの左上端であり、 xの値が増えるにつれ右に移動する。また、yの値が増えるにつれ下に移動する。 数学でグラフを描画する場合と違い、yの値が下に向いていることに注意せよ。
※ Frameを使う場合、描画範囲にウインドウのタイトルバーが含まれるため、 原点(0,0)はタイトルバーの左上端となる。タイトルバーの場所は描画されないため、 実際に描画できるのはyがタイトルバーの幅を上回った所からとなる。例えば、 g.drawLine(0,0,100,100)などを実行すると確認できる。

drawRectは長方形(矩形)を描画するメソッドである。引数をそれぞれ x1, y1, w, h とすると、 始点を(x1,y1)とし、幅をh、高さをhとした長方形を描画する。 長方形の辺はx軸もしくはy軸と並行になる。また始点は長方形の左上の頂点の座標である。 例の、

g.drawRect(70,70,60,60)

では、 始点が(70,70)、幅と高さが60となる。つまり、(70,70)-(130,130)の長方形 を描画することになる。

drawOvalは楕円を描画するメソッドである。引数をそれぞれ x, y, w, h とすると、 始点を(x,y)とし幅w,高さhとした長方形に囲まれる最大の楕円を描画する。 幅と高さを同じ値にした場合は真円を描画することになる。例の、

g.drawOval(50,50,100,100)

では、 始点が(50,50)、幅と高さが100となる。つまり、(50,50)-(150,150)の長方形で 囲まれた真円となる。いいかえると、中心が(100,100)、半径が50の円となる。

色を指定する

上記のプログラムでは、図形は黒で描画されたはずである。 この描画色を変更することができる。 以下のプログラム ColorTest.java では色の指定をしている。

import java.awt.*;
import java.awt.event.*;

class ColorFrame extends Frame {
    public ColorFrame() {
	setSize(200, 200);
	addWindowListener(new MyWindowAdapter());
	setBackground(Color.yellow);
    }
    public void paint(Graphics g) {
	g.setColor(Color.red);
	g.drawLine(50,50,150,150);
	g.drawLine(50,150,150,50);
	g.setColor(Color.green);
	g.drawOval(50,50,100,100);
	g.setColor(new Color(0,0,255));
	g.drawRect(70,70,60,60);
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
	System.exit(0);
    }
}

public class ColorTest {
    public static void main(String[] args) {
	ColorFrame f = new ColorFrame();
	f.setVisible(true);
    }
}

まず、グラフィックス描画に使われる色には、前景色と背景色がある。 前景色は線や四角形などの図形を描画する時に使用する色である。 また、背景色はウインドウなどに何も描かれていない状態で表示されている色である。

前景色を変更するには、Graphicsオブジェクトに対してsetColorメソッドを呼び出す。 引数には設定したい色を指定するが、これはColorオブジェクトである。 Colorオブジェクトの指定方法として主に以下の2つがある。

  1. 最初から用意されている色を使う
    以下のように赤、青、黒といった良く使われる色は最初から用意されている。
    説明
    Color.black黒色
    Color.blue青色
    Color.cyanシアン色
    Color.darkGray濃い灰色
    Color.gray灰色
    Color.green緑色
    Color.lightGray薄灰色
    Color.magentaマゼンタ色
    Color.orangeオレンジ色
    Color.pinkピンク色
    Color.red赤色
    Color.white白色
    Color.yellow黄色
    なお、Color.BLACKのように大文字による書き方もできる(JDK1.4以降)。
  2. RGB値を指定して自由に色を指定する
    色は光の三原色のそれぞれの光強度を指定することで表現できる。 RGB値の指定には、Colorクラスのコンストラクタを使う (例: new Color(0, 128, 128))。 引数は順番にR(赤成分),G(緑成分),B(青成分)の値を意味し、 0〜255の値を取り得る。

背景色を指定するには、そのコンポーネントに対して setBackgroundメソッド を呼び出す。引数には、前景色と同様にColorオブジェクトを指定する。 例えば、上記のプログラムでは、ColorFrameクラスのコンストラクタの中で 背景色を黄色に指定している。
※ 前景色を指定するのに、背景色と同様にコンポーネントに対して setForegroundメソッドを呼ぶ方法がある。ただし、paintメソッドの中で setForegroundメソッドを呼んではならない。setForegroundメソッドでは repaintメソッドを呼んでしまうので結果的にpaintメソッドを何重にも 呼んでしまいJavaが高負荷になってしまうからである。

画像を表示する

以下のプログラム ImageTest.java は、画像ファイルを読み込んで それを表示する。
import java.awt.*;
import java.awt.event.*;

class ImageFrame extends Frame {
    Image im;
    public ImageFrame() {
	setSize(280, 300);
	addWindowListener(new MyWindowAdapter());
	Toolkit tk = Toolkit.getDefaultToolkit();
	im = tk.getImage("usb-plugs.jpg");
    }
    public void paint(Graphics g) {
	g.drawImage(im, 20, 50, this);
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
	System.exit(0);
    }
}

public class ImageTest {
    public static void main(String[] args) {
	ImageFrame f = new ImageFrame();
	f.setVisible(true);
    }
}
サンプルの画像ファイル ← この画像ファイルをJavaのプログラムを保存してあるディレクトリに保存して使用せよ。
プログラムの説明

AWTでは、JPEG, PNG, GIF形式の画像ファイルを読み込み表示することができる。 画像を取り扱うにはImageクラスを使う。まず、画像ファイルを読み込み Imageオブジェクトを生成する。

Toolkit tk = Toolkit.getDefaultToolkit();
im = tk.getImage("usb-plugs.jpg");

ここでは、Toolkitオブジェクトを使用している。 Toolkit.getDefaultToolkit()で、通常使われるToolkitオブジェクトを取得し、 そのオブジェクトのgetImageメソッドを呼び出すことで画像ファイルを読み Imageオブジェクトを返している。 引数の"usb-plugs.jpg"は読み込みたい画像ファイルである。

画像の描画は、paintメソッド内で行なっている。

g.drawImage(im, 20, 50, this);

Graphicsオブジェクトに対し、drawImageを呼び出している。 drawImageの引数は、それぞれ画像を示すImageオブジェクト、x座標、y座標、 ImageObserverオブジェクトである。xとyはそれぞれ、画像の左上端の座標である。 ImageObserverオブジェクトとしては、コンポーネント、つまりこの場合 this を指定すればよい。

マウスのイベントを処理する

以下のプログラム MouseEventTest.java はマウスイベントを処理するプログラムである。

import java.awt.*;
import java.awt.event.*;

class MouseEventFrame extends Frame {
    public MouseEventFrame() {
	setSize(280, 300);
	addWindowListener(new MyWindowAdapter());
	addMouseListener(new MyMouseAdapter());
	addMouseMotionListener(new MyMouseMotionAdapter());
    }
}

class MyMouseAdapter extends MouseAdapter {
    public void mousePressed(MouseEvent e) {
	System.out.println("Pressed at ("+e.getX()+","+e.getY()+")");
    }
    public void mouseClicked(MouseEvent e) {
	System.out.println("Clicked at ("+e.getX()+","+e.getY()+")");
    }
    public void mouseReleased(MouseEvent e) {
	System.out.println("Released at ("+e.getX()+","+e.getY()+")");
    }
}

class MyMouseMotionAdapter extends MouseMotionAdapter {
    public void mouseMoved(MouseEvent e) {
	System.out.println("Moved at ("+e.getX()+","+e.getY()+")");
    }
    public void mouseDragged(MouseEvent e) {
	System.out.println("Draged at ("+e.getX()+","+e.getY()+")");
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
	System.exit(0);
    }
}

public class MouseEventTest {
    public static void main(String[] args) {
	MouseEventFrame f = new MouseEventFrame();
	f.setVisible(true);
    }
}

マウスイベントは、マウスボタンを押した、離した、クリックしたなど、 低レベルのイベントである。つまり、マウスというハードウェアに近いイベントと言える。これに対し、ボタンで使用したアクションイベントは、高レベルのイベントである。これは、GUIとして画面上に構成したボタンを押したという、アプリケーションソフトに近いイベントと言える。

マウスイベントには、マウスボタンを押した、離したというマウスの動き以外のイベントと、マウスを移動した時に発生するイベントの2種類に分かれており、それぞれ MouseListener, MouseMotionListenerといったイベントリスナーが対応する。 つまり、MouseListenerやMouseMotionListenerといったインタフェースを実装することでイベントを処理することができる。あるいは、MouseAdapterもしくはMouseMotionAdapterというアダプタクラスが用意されており、それらを継承してイベント処理することもできる。

MouseListenerとMouseMotionListenerには、それぞれ以下のようなイベント処理のメソッドが宣言されている。

MouseListenerに用意されているイベント処理メソッド
メソッド名イベントの説明
mouseClickedマウスボタンをクリックした(押してから離した)
mouseEnteredコンポーネントにマウスが入った
mouseExitedコンポーネントからマウスが出た
mousePressedマウスボタンを押した
mouseReleasedマウスボタンを離した

MouseMotionListenerに用意されているイベント処理メソッド
メソッド名イベントの説明
mouseDraggedマウスボタンを押したままドラッグした(移動した)
mouseMovedマウスボタンを押さずにマウスを移動した

マウスの現在の位置は、イベント処理メソッドの引数であるMouseEventオブジェクトに対し、getX もしくは getYメソッドを呼ぶことで得られる。それぞれX座標とY座標である。

キーボードのイベントを処理する

以下のプログラム KeyEventTest.java はキーイベントを処理するプログラムである。

import java.awt.*;
import java.awt.event.*;
class KeyEventFrame extends Frame implements KeyListener {
    char key='\0';
    public KeyEventFrame() {
	setSize(280, 300);
	addWindowListener(new MyWindowAdapter());
	addKeyListener(this);
	requestFocus();
    }
    public void paint(Graphics g) {
	g.drawString(""+ key, 20,100);
    }
    public void keyTyped(KeyEvent e) {
        System.out.println("Typed '"+e.getKeyChar()+"'");
	key = e.getKeyChar();
	repaint();
    }
    public void keyPressed(KeyEvent e) {}
    public void keyReleased(KeyEvent e) {}
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }
}
public class KeyEventTest {
    public static void main(String[] args) {
	KeyEventFrame f = new KeyEventFrame();
	f.setVisible(true);
    }
}

キーボードのイベントは、KeyListenerというイベントリスナーで処理する。 KeyListenerのイベント処理メソッドは以下の通りである。

メソッド名イベントの説明
keyPressedキーを押した
keyReleasedキーを離した
keyTypedキーをタイプした

キーをタイプしたというのは、キーを押したというのより高レベルのイベントで、 何か文字を入力した場合に、発生する。キーを押したというのは、 例えば、Shiftキーを押したなど、文字入力にいたっていないレベルのイベントも処理できる。単に文字入力を処理するなら、キータイプのみ処理すれば良いが、 ShiftやCtrlといった装飾キーを押した時の検知も必要な場合は、キーを押したイベントの処理もする必要がある。

なお、押した文字を得るには、KeyEventオブジェクトに対して、getKeyCodeメソッドを呼び出せば良い。

課題 5-1

上のプログラムDrawGraphicsTest.javaを修正して以下のような図を描くプログラムを作成せよ。

ただし、三角形の頂点は(100,70),(150,120),(50,120)とし、楕円の幅を100、 高さを60とする。また長方形の幅を100、高さを50とする。

課題 5-2

課題5-1で作成したプログラムを修正して、長方形の中を塗り潰すようにせよ。 使うメソッドはAPI仕様などで調べよ。

課題 5-3

上のプログラムColorTest.javaを修正して、背景を黒色、×印を青色、円をピンク色、 正方形を灰色にせよ。

課題 5-4

上のプログラムImageTest.javaを修正して、サンプルの画像ファイルを横に二つ並べて表示するようにせよ。同じ画像を並べれば良い。

課題 5-5

課題5-4で作成したプログラムを修正して、画像ファイルの大きさ(幅)が変わっても、正しく横に並ぶようにせよ。 なお、画像ファイルの幅を得るには、上記のプログラムの場合、 paintメソッド中で、im.getWidth(this) で求めれば良い。 Gimp等を使って違う大きさの画像を用意するか、この画像ファイルを使用して動作を確認せよ。

課題 5-6

上のプログラム MouseEventTest.javaを実行し、各種マウスイベントを観察せよ。 そして、MouseClickのイベントが発生するのは、どのような場合かを説明せよ。

課題 5-7

上のプログラム MouseEventTest.javaでアダプタクラスを使うのではなく、 MouseEventFrameがMouseListenerとMouseMotionListenerインタフェースを 実装することで、同様の動作をするプログラムを作成せよ。

課題 5-8

上のプログラムKeyEventTest.javaを修正して、 プログラム起動後に打った文字列を表示するようにせよ。
ヒント: 文字列の変数を用意し、打った文字をその文字列に追加していけば良い。

ソフトウエア基礎演習


ohmi@rsch.tuis.ac.jp (2004年5月17日〜2004年5月19日)