描画を保存して再描画したり、修正したりする方式に
- 描画の手順(あるいは、描画したものと位置)を保持(ドロー系)
- 描画した結果をイメージ(ビットマップ)として保持(ペイント系)
の2種類があります。
描画対象を取り扱うのに便利なクラスに Graphics2D と RectangularShape があります。
Ellipse2D.Double(楕円) と Rectangle.Double(矩形)を書く例を提示します。
この『楕円(や矩形)を書く』方法は、前節の swing コンポーネントへの描画とは少しニュアンスが異なります。
前節では例えば、
『(x,y)を左上隅にする幅w、高さh の矩形に内接する楕円を描け』
と命令したのですが、ここでは
『(x,y)を左上隅にする幅w、高さh の矩形に内接する楕円』を生成しておき、『それ』を描け、と命令します。
Graphics2D と Ellipse2D.Double(楕円)、Rectangle.Double(矩形)のクラス階層は次の通り。
java.lang.Object
├ java.awt.Graphics
│ └ java.awt.Graphics2D
└ java.awt.geom.RectangularShape
├ java.awt.geom.Ellipse2D
│ └ java.awt.geom.Ellipse2D.Double
└ java.awt.geom.Rectangle2D
└ java.awt.geom.Rectangle2D.Double
イメージとして扱うには、BufferedImage クラスを使います。
【例1】 楕円と矩形を RectangularShape の配列として保存する方法(ドロー)
paintComponent で描画しなければいけないものを RectangularShape の配列に保存しておき、
paintComponent がよばれたら、Graphics2D の draw メソッドで描画すると言う方法です。
ソース・プログラム Graph2d.java
描画の手順
- java.awt.geom.* を import しましょう。(4行目)
- Ellipse2D.Double と Rectangle2D.Double の親クラス RectangularShape の配列
shape(14行目) と 要素の個数 nshape (13行目)を定義します。
- コンストラクタの中で、マウスリスナを登録します。
ここでは、アダプタをつかった方法を用います。
オーバライドが必要な mouseReleased だけを定義するだけですみます。(17〜36行目)
- マウスカーソルの位置を取得し、double に変換(19〜20行目)
- 画面の下端に貼り付けたテキストフィールドから
図形の幅と高さをとりだし、double に変換(21〜22行目)
- 画面の下端のラジオボタンで指定された図形(楕円か矩形)を
生成し、s に代入(23〜28行目)
- 保存用の配列に s を代入(29〜33行目)
- 描画(34行目)
- paintComponent メソッドを定義します。(40〜47行目)
- g を Graphics2D に変換(42行目)
- gg に対して配列に保存した図形を draw します(45行目)。
- ラジオボタンをボタングループに登録し(69〜71行目)、
パネルに貼り付けます(72〜74行目)。
- ラベルで標題をつけて、テキストフィールドを貼り付けます(75〜78行目)。
クリックをした場所を左上隅にする図形が表示されます。
【例2】 描いた図形を BufferedImage として保存する方法(ペイント)
BufferedImage に描画しておき(書きためておき)、
paintComponent が呼ばれたら、その BufferedImage を画面にコピーする方法です。
ソース・プログラム Graph2p.java
描画の手順
- java.awt.image.BufferedImage を import しましょう。(5行目)
- BufferedImage クラスの変数 image と、image のGraphics2D を保持する
imageGraphics を宣言します。(13〜14行目)
- コンストラクタの中で、マウスリスナを登録します。(17〜31行目)
x、y、w、h の取得方法は例1と同じです。
- 画面の下端のラジオボタンで指定された図形(楕円か矩形)を
生成し、shape に代入(24〜28行目)
- 描画(29行目)
- paintComponent メソッドを定義します。(35〜51行目)
- 初回の描画のときは、このパネルの大きさと同じイメージを生成し、
image という名前をつけ(37〜40行目)、image の Graphics を
作成し、背景色で塗りつぶしておきます(41〜43行目)。
- shape に図形が設定されていればそれを image に描画します。(46〜48行目)
できた image をこのパネルに書き出します(49行目)。
- main メソッドは例1と同じです。
ペイント系の描画については
16.7 以降に例題があります。
この節では、以降ドロー系の描画の例を掲げます。
【例3】 描いた図形の移動(ドロー その2)
ドロー系の例として、描いた図形をマウスで移動してみましょう。
例1のプログラムに次のような変更を加えます。
行番号は プログラム Graph2d1.java の該当個所。
- 図形を draw のかわりに fill で描き、塗つぶす。76行目
- クリックした点を(左上隅とする図形ではなく)中心にした図形を描く。26、28行目
- 図形の中にマウスカーソルを入れてドラッグをすると、その図形を移動する。46〜52行目
- 画面外にドラッグした場合は、図形を削除する。55〜64行目
ソースプログラム Graph2d1.java
実行例
【例4】 色つき図形
色のついた図形を描画しましょう。
色の選択は、Appendix A Component の
java.swing.JColorChooser
の showDialog の使用例 (プログラム ColorButton6.java) を参考にします。
楕円や矩形の親クラス RectangularShape を継承するか委譲するかして、色付きのクラスを作ることになります。
つまり、
class ColoredShape extends RectangularShape { // 継承
Color color;
...
}
とするか、
class ColoredShape {
RectangularShape shape; // 委譲
Color color;
...
}
とするかのいずれかの方法があります。
ColoredShape と RectangularShape の関係が is-a 関係とも has-a 関係ともいえますので
(注1)
いずれの方法でもよいのですが、
ここでは後者のやり方を採用しました。前者のやり方は例5で挑戦しましょう。
注1
is-a : ColoredShape は RectangularShape に Color 属性を追加したもの
has-a : ColoredShape は RectangularShape 属性 と Coror 属性をもっている
例3のプログラムに次のような変更を加えます。
行番号は プログラム Graph2d2.java の該当個所。
- ColoredShape というクラスを定義し、色と図形の対からなるクラスとする。7〜8行目
- 図形を保存する配列 shape は、RectangularShape[] から ColoredShape[] に変更する。48行目
- ColoredShape クラスに必要なメソッドを追加する。15〜37行目
shape[]. ... という形で参照しているメソッド
(例3では RectangularShape クラスのメソッドであったものが、
ここでは ColoredShape のメソッドとなっている)を定義し、
RectangularShape の同名のメソッドを呼び出す。19〜37行目
- 図形を作成したら、色を指定して ColoredShape を生成し保存する。65行目
- メソッド main にカラーチューザを呼び出すボタンを追加する。123〜130行目
ソースプログラム Graph2d2.java
Graph2d1.java との差異
(例3の Graph2d1.java からの変更個所を赤色で表示)
実行例
【例5】 色つき図形(interface の拡張)
RectangularShape が抽象クラスですので、単に extends RectangularShape とすると、
コンストラクタを自分で書くはめになります。
ここででは、interface Shape を拡張して、getColor() が使えるようにしておいて、
Ellipse2D.Double を継承したクラスと Rectangle2D.Double を継承したクラスを
定義しました。
インターフェースの拡張はこの講座の範囲外ですが、プログラムを提示しておきます。
ソースプログラム Graph2d2x.java
実行結果は例4と同じです。
Graph2d1.java との差異
(例3の Graph2d1.java からの変更個所を赤色で表示)
【例6】 色つき図形(interface の拡張)
【例7】 枠の描画か塗りつぶしかの選択
【例8】 三角形
三角形も描けるようにしてみましょう。
例5 でいやがっていた
RectangularShape の拡張をすることになります。
左図のように点(x,y) を中心とする幅w、高さhの矩形内に内接する、底辺がy軸に並行な二等辺三角形を
を描くことにします。
重なりや包含関係は、この矩形領域で判定することにします。
プログラム Grapn2d2s.java に上向き、下向きの2種類の二等辺三角形を追加します。
プログラム Graph2d2z.java
Graph2d2s.java からの変更点(赤い行番号)
以下の行番号は プログラム Graph2d2z.java の該当個所。
- java.awt.geom の import が増えました。
個別に import してみましょう。4〜10行目
- クラス名の変更。186、199、272行目
- 上向き、下向きの三角用のボタンを用意し、選択されたら、shapetype をそれぞれ 2、3
とするようアクションリスナを登録する。294〜305行目
- 以前からあるボタングループ bg に追加し(315〜316行目)、
パネル southpan にも貼り付ける。319〜320行目
ボタンが増えたのでウィンドウの幅も広げておく。327行目
- x、y、w、h および 三角形が上向きか下向きかをあらわす ud が与えられたとき、
三角形の輪郭を描く PathIterator を定義する。57〜111行目
- この PathIterator で定まる GenaralPath で三角形を表現する。
118〜119、135〜136行目
実行例