3.2 図を描く
3.2.1 外枠と内面
Component クラスの paint() メソッドでは、Graphics クラスのインスタンスを用いて様々な図形を描画することができます。Graphics クラスの描画関連メソッドは主に draw*() メソッドと fill*() メソッドに分かれます。draw*() 系のメソッドは矩形の外側の枠を 1 ピクセルの線で描画し、fill*() 系のメソッドは矩形の内面を塗りつぶします。
メソッド | 昨日 |
---|---|
public abstract void drawLine(int x1,int y1,int x2,int y2) | 座標系の点 (x1, y1) と点 (x2, y2) との間に線を描画する。 |
public void drawRect(int x,int y,int width,int height) | 矩形の輪郭を描画します。 |
public abstract void drawRoundRect(int x,int y,int width,int height,int arcWidth,int arcHeight) | 丸い輪郭の矩形を描画する。 |
public void draw3DRect(int x,int y,int width,int height,boolean raised) | 矩形の輪郭を 3D で強調表示して描画する。 |
public abstract void drawOval(int x,int y,int width,int height) | 楕円の輪郭を描画する。 |
public abstract void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) | 円弧または楕円弧の輪郭を描画する。 |
public abstract void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) | 連続的につながった直線を描画する。 |
public abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) | 閉じた多角形を描画する。 |
public void drawPolygon(Polygon p) | |
public abstract void fillRect(int x, int y, int width, int height) | 矩形を塗りつぶす。 |
public abstract void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) | 角の丸い矩形を塗りつぶす。 |
public void fill3DRect(int x, int y, int width, int height, boolean raised) | 強調表示された 3 次元矩形を塗りつぶす。 |
public abstract void fillOval(int x, int y, int width, int height) | 楕円を塗りつぶす。 |
public abstract void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) | 円弧または楕円弧を塗りつぶす。 |
public abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) | 閉じた多角形を塗りつぶす。 |
public void fillPolygon(Polygon p) |
表1のメソッドを駆使すれば、ほとんどの図形を表現することができるでしょう。図形の原点は文字列同様にクライアント座標を指定します。width と height には幅と高さを指定します。この値は図の座標ではなく、指定した原点 (x , y) から相対的な幅と高さです。例えば、X 座標 10、Y 座標 10 の位置から幅 200 ピクセル、高さ 100 ピクセルの長方形を指定した場合、長方形の右下は X 座標が 210、Y 座標が 110 の位置となります。
楕円や楕円弧などでも描画する位置となる座標とサイズを指定します。楕円や楕円弧の図は、矩形全体をカバーする円となります。これは、円の中心が必ず長方形の幅と高さの半分の位置(中心)になることを表します。
import java.applet.Applet; import java.awt.*; //<applet code="Test.class" width="400" height= "400"></applet> public class Test extends Applet { public void paint(Graphics g) { g.drawLine(10 , 10 , 200 , 50); g.drawRect(10 , 10 , 200 , 50); g.drawOval(10 , 10 , 200 , 50); g.fillRect(220 , 10 , 200 , 50); g.fillOval(10 , 70 , 200 , 50); g.fillArc(220 , 70 , 200 , 50 , 0 , 300); } }
コード1は、上野実行結果のように図形を描画するアプレットです。最初の drawLine() メソッドでは (10 , 10) 座標から (200 , 50) 座標までの線を描画しています。一方 drawRect() と drawOval() は (10 , 10) 座標から幅 200、高さ 50 の長方形を描画しています。この範囲は、2点の座標を指定している drawLine() メソッドとは異なり矩形の原点 (10 , 10) から数えて幅 200 ピクセル、高さ 50 ピクセルの図形を描画します。長方形を表現するために Java では点ではなく幅と高さを求めている事実は、drawLine() で描画した線が長方形の右下に到達していないことで確認することができます。
さらに、このプログラムでは矩形と楕円、及び楕円弧を fill*() 系の関数を使って描画しています。fill*() 系の関数は内面を塗りつぶすため、図のように黒く塗りつぶされた矩形、楕円、楕円弧が描画されたのです。
3.2.2 多角形
複数の線、または多角形を描画するには Graphics クラスの drawPolyline() や drawPolygon()、または fillPolygon() メソッドなどを用いて描画します。描画メカニズムは同じですが、これらのメソッドは多角形の頂点を表す座標を配列で渡さなければならないので少し煩雑です。これらのメソッドでは、最初の 2 つの引数に X 座標と Y 座標それぞれの位置を表す配列を指定し、その後に点の総数を指定します。
例えば 5 つの頂点を持つ五角形を描画する場合、xPoints と yPoints にはそれぞれ少なくとも 5 以上のサイズを持つ数値型の配列を指定します。五角形の最初の頂点は (xPoints[0] , yPoints[0]) となります。そして、最後の引数に渡された nPoints までの多角形が配列から選択されます。
drawPolygon() や fillPolygon() メソッドはオーバーロードされているので、配列以外に Polygon オブジェクトで多角形を描画する方法を選択することもできます。java.awt.Polygon クラスは、X 座標と Y 座標を表す値をペアを管理し多角形を提供するためのクラスです。
java.lang.Object | +--java.awt.Polygon
public class Polygon extends Object implements Shape, Serializable
このクラスは幾何学模様を提供する java.awt.Shape インタフェースの実装でもあります。Polygon() コンストラクタは次のように定義されています。
public Polygon()
public Polygon(int[] xpoints, int[] ypoints, int npoints)
オーバーロードされた引数を受け取るコンストラクタは drawPolyline() 関数と同じ意味で、座標を表すペアの数値型配列と、頂点の総数を指定します。あらかじめ多角形の形が決まっているのならば、配列を渡すコンストラクタを呼び出すことで多角形を表現できます。引数を指定しないコンストラクタの場合、空の多角形が生成されます。
Polygon クラスのすばらしいところは、頂点の座標を動的に追加することができることです。Java には ArrayList クラスがあるので動的な配列を作ることは難しくありませんが、座標をペアで取り扱ってくれるため Polygon クラスを使ったほうが簡単でしょう。Polygon オブジェクトに新しい座標を追加するには addPoint() メソッドを使います。
public void addPoint(int x, int y)
x と y には追加する頂点の X 座標と Y 座標を指定します。このメソッドを使えば動的に新しい頂点を追加することができるため、配列よりも動的に多角形を表現することができるようになります。
import java.applet.Applet; import java.awt.*; //<applet code="Test.class" width="400" height= "400"></applet> public class Test extends Applet { private int[] xPoints = { 10 , 10 , 100 , 100 , 200 , 200 , 10 }; private int[] yPoints = { 10 , 200 , 200 , 100 , 100 , 10 , 10 }; public void paint(Graphics g) { /*配列を使って複数の線を描画*/ g.drawPolyline(xPoints , yPoints , xPoints.length); /*Polygon を使って多角形を描画*/ Polygon polygon = new Polygon(); polygon.addPoint(210 , 10); polygon.addPoint(210 , 110); polygon.addPoint(110 , 110); polygon.addPoint(110 , 200); polygon.addPoint(300 , 200); polygon.addPoint(300 , 10); g.fillPolygon(polygon); } }
コード2は、各店の座標を格納した数値型配列を渡す方法を用いて複数の線を描画する drawPolyline() メソッドと、Polygon オブジェクトから多角形を塗りつぶす fillPolygon() メソッドを使って 2 つの多角形を描画しています。数値型配列を用いる方法は配列初期化子で点の座標を一度に指定していますが、Polygon クラスを使えば、頂点をいつでも addPoint() で追加することができるため動的に処理することができます。
3.2.3 位置とサイズと矩形
楕円や円弧も含めて、Graphics クラスで描画する多くの図は矩形の情報を基にしています。Java における矩形は矩形の開始座標と矩形のサイズで構成されています。座標もサイズも 2 つの数値のペア (x , y) と (width , height) で構成されるため、これらをカプセル化するクラスが求めらるでしょう。
座標は java.awt.Point クラスで表現することができます。
java.lang.Object | +--java.awt.geom.Point2D | +--java.awt.Point
public class Point extends Point2D implements Serializable
スーパークラスの Point2D クラスは Java2D API の一部である java.awt.geom パッケージに分類されています。Point2D クラスは 2 次元空間の座標を表す全てクラスの抽象スーパークラスです。Point クラスは最も基本的な int 型で座標を表現しますが、この他に浮動小数点を使って座標を処理する座標クラスを定義する場合などに Point2D 抽象クラスを継承します。
Point() コンストラクタは次のようなものが定義されています。
public Point()
public Point(Point p)
public Point(int x, int y)
Point オブジェクト p をコンストラクタに渡した場合は、その Point オブジェクトと同じ座標で初期化されます。引数を指定しないコンストラクタは、原点 (0 , 0) で初期化されます。2 つの数値 x と y を渡した場合は座標 (x , y) で Point オブジェクトが作られます。Point オブジェクトは座標を表す数値を x フィールドと y フィールドで提供します。
サイズに関する情報を提供するには、java.awt.Dimension クラスを用います。基本的な概念は Point クラスと同じで、Dimension クラスは幅 width と高さ height を表す数値をカプセル化しています。
java.lang.Object | +--java.awt.geom.Dimension2D | +--java.awt.Dimension
public class Dimension extends Dimension2D implements Serializable
スーパークラスの Dimension2D は幅と高さをカプセル化するクラスの抽象スーパークラスで、サイズを現す実際に記憶方式は Point2D クラスと同様にサブクラスに委ねています。Dimension クラスは数値でサイズを表現する Dimension2D クラスの代表的な実装例と言えるでしょう。
public Dimension()
public Dimension(Dimension d)
public Dimension(int width, int height)
Dimension() コンストラクタは Point() コンストラクタに酷似しています。引数の無いコンストラクタは幅と高さを 0 で初期化し、Dimension オブジェクトを受けるコンストラクタはオブジェクトと同じ幅と高さで初期化します。width と height を指定するコンストラクタは、指定した幅と高さでオブジェクトを初期化します。Dimension オブジェクトは幅を表す width フィールドと高さを表す height フィールドを提供します。
Point と Dimension クラスの情報を組み合わせることで、座標とサイズからなる矩形を表現することができます。これを実現するのが java.awt.Rectangle クラスです。 このクラスは Point クラス同様に Shape インタフェースの実装です。
java.lang.Object | +--java.awt.geom.RectangularShape | +--java.awt.geom.Rectangle2D | +--java.awt.Rectangle
public class Rectangle extends Rectangle2D implements Shape, Serializable
RectangularShape は矩形を含め、楕円や円弧、角の丸い矩形など、矩形の情報を基本に作られる Shape インタフェースを実装する抽象スーパークラスです。Rectangle2D クラスはこのうち座標とサイズで構成される矩形を提供するクラスの抽象スーパークラスとなります。Rectangle クラスは数値型で矩形の情報を提供する Rectangle2D クラスの実装例です。
Java のデザインはこのように、インタフェース空抽象クラスに、抽象クラスから実装クラスへと流れるパターンが多く使われています。これはオブジェクト指向による設計論でも最も基本的な手法で、実体を抽象化して操作することが可能であり、実体を隠蔽して開発を進められる利点があります。これまでの例でも、何らかの形を提供する Shape インタフェースから、具体的な形を提供する Rectangle2D 抽象クラス、そして Rectangle2D を数値型で実現する実装クラスへと流れています。オブジェクト指向の初心者には煩雑な手続きに感じるかもしれませんが、大規模ライブラリに必要な柔軟性を確保した高度な設計であると評価できます。
import java.applet.Applet; import java.awt.*; //<applet code="Test.class" width="400" height= "400"></applet> public class Test extends Applet { private Point pt = new Point(10 , 10); private Dimension size = new Dimension(200 , 100); private Rectangle rect = new Rectangle(100 , 120 , 100 , 50); public void paint(Graphics g) { g.drawRect(pt.x , pt.y , size.width , size.height); g.drawRect(rect.x , rect.y , rect.width , rect.height); } }
コード3は、座標はサイズを保存した変数を使って図形を描画するプログラムです。実行結果の上の長方形は Point オブジェクト pt と Dimension オブジェクト size が指す座標とサイズから、下の長方形は Rectangle オブジェクト rect が指す座標とサイズから描画したものです。