■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2007年06月24日

    楽しいJava講座 - 初心者から達人へのパスポート
                  vol.059

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.グラフィックスのプログラミング
========================================================

今回は、お絵描きソフトに直線や矩形、楕円を描けるように
機能を追加しましょう。

その前に、前回GraffitiAppletに貼り付けたJList(strokeList)
がちょっと好ましくない振る舞いをすることに気づいたでしょう
か。

では、Eclipseを起動し、GraffitiAppletを起動してテストして
みてください。
現在のGraffitiAppletの中のJListでは、複数選択ができてしま
いますね。
たとえば1行目をクリックし、Ctrlキーを押しながら3行目をクリ
ックすると1行目と3行目が同時に選択状態になりますし、1行目
をクリックし、Shiftキーを押しながら3行目をクリックすると
1行目から3行目までのすべてが同時に選択状態になります。

前回のソース・コードの編集内容ではJList上で1つの項目だけが
選択されることを前提にしていますので、複数選択ができてしま
っては困ります。
複数選択ができてしまうのは、JListのselectionModeプロパティー
がデフォルトでは「MULTIPLE_INTERVAL」になっているからです。

ではGraffitiAppletのVEエディターを開き、JList(strokeList)
をクリックし、「プロパティー」ビューを開いてください。

selectionModeプロパティーの値が「MULTIPLE_INTERVAL」になっ
ているので、これを「SINGLE」に変更しましょう。

保管して、再度GraffitiAppletを起動してテストしてみてください。
複数選択はできなくなりましたね。


┌補足─────────────────────────┐
JListのselectionModeプロパティーの値には、次の3種類が
あります。

SINGLE_SELECTION
  選択できるのは一つだけです。Eclipseの「プロパティー」
  ビューではSINGLEと表示されます。

SINGLE_INTERVAL_SELECTION
  選択できるのは一つまたは、Shiftキーを押しながら指定で
  きる連続した範囲一つだけです。Eclipseの「プロパティー」
  ビューではSINGLE_INTERVALと表示されます。

MULTIPLE_INTERVAL_SELECTION
  選択できるのは複数(Ctrlキーを押しながら選択すれば複数
  指定できる)。またShiftキーを押しながら指定できる連続
  した範囲も複数指定できます。Eclipseの「プロパティー」ビ
  ューではMULTIPLE_INTERVALと表示されます。


これらはListSelectionModelというjavax.swingパッケージにはい
っているインターフェースにint型のフィールドとして定義されて
います。(インターフェースのフィールドですからpublicでstatic
でfinalです。)
└───────────────────────────┘


では、続いてお絵描きソフトに直線や矩形、楕円を描く機能を追加
する作業にはいりましょう。

これらの図形は、DecoratedLinesクラスと同様にそれぞれDecoratedLine、
DecoratedRectangle、DecoratedEllipseというクラスで表現すること
にします。

ところが、直線、矩形、楕円は、前回までに描いていた自由曲線とは
大きな違いがあります。
それは、自由曲線(DecoratedLines)が複数のLine2Dオブジェクトを
Vectorに入れて表現するのに対し、直線(DecoratedLine)、矩形
(DecoratedRectangle)、楕円(DecoratedEllipse)はVectorなど
使わずに純粋に単一の図形だけを表現するという違いです。
(なお、数学用語としての「直線」は両端のない無限に長い真っ直ぐ
な線を意味しますが、当メールマガジンで直線と呼ぶものは数学的な
直線ではなく、日常的な用語としての直線(両端のある直線、つまり
数学用語では「線分」と呼ばれているもの)のことを意味するものと
します。)

さて、これらの単一の図形(直線、矩形、楕円)を表現するクラスと
して、それぞれLine2D、Rectangle(またはRectangle2D)、Ellipse2D
というクラスがJavaのAPIに用意されています。
Line2D、Rectangle2D、Ellipse2Dはjava.awt.geomパッケージにはいっ
ています。
またRectangleはjava.awtパッケージにはいっています。

これらのクラスはすべてShapeというインターフェース(java.awtパッ
ケージにはいっている)を実装していますので、Shapeを通して共通に
メソッドの呼び出しができます。

実際に、これらの単一の図形は、どちらも描画するにはGraphics2Dの
draw()メソッドを使い、描画の色はGraphics2DのsetColorメソッドで
設定し、線の種類はGraphics2DのsetStroke()メソッドで設定すると
いうように共通のメソッド呼び出しで実行できます。

したがって、これらを一つのグループに属するクラスと見なし、その
共通部分をスーパークラスに定義することにします。
そしてそのスーパークラスの名前はDecoratedShapeと呼ぶことにしま
す。

というわけで、クラスDecoratedShapeを以下のようなソース・コード
にしてみましょう。DecoratedLinesと同じ要領でこのクラスを作って
みてください。内容もよく似ているので、DecoratedLinesをコピーし
て作ると効率がいいでしょう。

--------------------------------------------------------
package jp.co.flsi.lecture.cg;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;

public abstract class DecoratedShape {
   private Color color = Color.BLACK;
   private Stroke stroke = new BasicStroke(0.1f);
   private Shape shape = null;

   public Color getColor() {
      return color;
   }

   public Shape getShape() {
      return shape;
   }

   public Stroke getStroke() {
      return stroke;
   }

   public void setColor(Color clr) {
      color = clr;
   }

   public void setShape(Shape shp) {
      shape = shp;
   }

   public void setStroke(Stroke strk) {
      stroke = strk;
   }
  
   public void paintSelf(Graphics2D g2d) {
      g2d.setColor(getColor());
      g2d.setStroke(getStroke());
      g2d.draw(getShape());
   }

}
--------------------------------------------------------

このクラスは特定の図形を表さない抽象的なものなので、インスタンス
を作ったりしないようにabstractを指定してあります。つまり抽象クラ
スにしています。

なお、各メソッドについては、Vectorを持たないことを除けばDecoratedLines
クラスの場合とほとんど同じなので説明の必要はないでしょう。


続いて、DecoratedLine、DecoratedRectangle、DecoratedEllipseは
DecoratedShapeのサブクラスとして作成します。それぞれ下記のような
ソース・コードにして作成しましょう。


[DecoratedLineのソース・コード]
--------------------------------------------------------
package jp.co.flsi.lecture.cg;
import java.awt.geom.Line2D;

public class DecoratedLine extends DecoratedShape {
  
   public DecoratedLine() {
      setShape(new Line2D.Float());
   }

   public DecoratedLine(int x0, int y0, int x1, int y1) {
      setShape(new Line2D.Float(x0, y0, x1, y1));
   }
  
}
--------------------------------------------------------

Line2D.Float()コンストラクターは前に出てきましたが、4つの引数を
持つものは、前の2つの引数が線分の始点のx座標とy座標で、後ろの2つ
の引数が終点のx座標とy座標を指定するものです。
なお、FloatというのはLine2Dの内部に組み込まれた内部クラスですが、
他にDoubleという内部クラスもあります。そしてLine2Dというクラスは、
float型の座標値を使って図形を作成したいときはFloatのコンストラク
ターを使い、double型の座標値で図形を作成したいときはDoubleのコンス
トラクターを使うという使い分け方式でインスタンス生成を行います。

なお、上のソース・コードの場合、引数はint型からfloat型へ暗黙の
型変換が行われますので、int型の変数を引数に与えても大丈夫です。


[DecoratedRectangleのソース・コード]
--------------------------------------------------------
package jp.co.flsi.lecture.cg;
import java.awt.Rectangle;

public class DecoratedRectangle extends DecoratedShape {
  
   public DecoratedRectangle() {
      setShape(new Rectangle());
   }

   public DecoratedRectangle(int x0, int y0, int width, int height) {
      setShape(new Rectangle(x0, y0, width, height));
   }
  
}
--------------------------------------------------------

Rectangle()コンストラクターの4つの引数を持つものは、前の2つの引数
がその矩形(長方形や正方形)の始点のx座標とy座標で、後ろの2つの
引数が横幅と高さを指定するものです。なお、始点は矩形の左上の隅に
取られます。


[DecoratedEllipseのソース・コード]
--------------------------------------------------------
package jp.co.flsi.lecture.cg;
import java.awt.geom.Ellipse2D;

public class DecoratedEllipse extends DecoratedShape {
  
   public DecoratedEllipse() {
      setShape(new Ellipse2D.Float());
   }

   public DecoratedEllipse(int x0, int y0, int width, int height) {
      setShape(new Ellipse2D.Float(x0, y0, width, height));
   }
  
}
--------------------------------------------------------

Ellipse2D.Float()コンストラクターの4つの引数を持つものは、その楕円
が内接する矩形に対し前の2つの引数がその矩形の始点のx座標とy座標で、
後ろの2つの引数が横幅と高さを指定するものです。なお、始点は矩形の
左上の隅に取られます。


以上、DecoratedLine、DecoratedRectangle、DecoratedEllipseはコンスト
ラクターの定義しかなくて、他のメソッドはスーパークラス(DecoratedShape)
のものを継承しているだけです。
つまり、これらのクラスは図形が異なることを除けは他の振る舞いはすべて
同じというわけです。


では、これらのクラスを使って図形を描けるようにGraffitiPanelと
GraffitiAppletを編集しましょう。


まずは、GraffitiPanelのソース・コードを以下のように編集しましょう。

(1) DecoratedLine、DecoratedRectangle、DecoratedEllipseのオブジェクト
を複数保管しておくためのVectorオブジェクトとして下記のフィールドを追加
しましょう。
freeCurvesフィールドの下に追加するとわかりやすいでしょう。

--------------------------------------------------------
private Vector<DecoratedShape> shapes = new Vector<DecoratedShape>();
--------------------------------------------------------

Vectorに保管するオブジェクトの型としてはスーパークラスのDecoratedShape
を指定したので、DecoratedLine、DecoratedRectangle、DecoratedEllipseの
うちのどのオブジェクトでも保管することができます。


(2) どの図形を描く状態であるかを示すフィールドを下記のように追加しま
しょう。
freeCurveFlagフィールドの下に追加するとわかりやすいでしょう。

--------------------------------------------------------
private boolean lineFlag = false;
private boolean ellipseFlag = false;
private boolean rectFlag = false;
--------------------------------------------------------


(3) initialize()メソッドの中の

--------------------------------------------------------
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
   public void mouseDragged(java.awt.event.MouseEvent e) {
      if(freeCurveFlag) {
         pts[ptIndex] = new Point(e.getX(), e.getY());
         ptIndex++;
      }
      repaint();
   }
});
--------------------------------------------------------

の部分を下記のように編集しましょう。

--------------------------------------------------------
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
   public void mouseDragged(java.awt.event.MouseEvent e) {
      if(freeCurveFlag) {
         pts[ptIndex] = new Point(e.getX(), e.getY());
         ptIndex++;
      }
      else {
         endPoint = new Point(e.getX(), e.getY());
      }
      repaint();
   }
});
--------------------------------------------------------

elseのブロックが追加されただけですね。
自由曲線の場合はドラッグする途中の点(MouseEventが検出された点)
をすべて記憶しておく必要がありますが、直線、矩形、楕円の場合は
始点(startPoint)と終点(endPoint)の2点だけ記憶すれば描けます
から、(そのうちのstartPointはmousePressed()で記憶されていますので)
endPointの記憶だけを行っています。


(4) paint()メソッド

--------------------------------------------------------
public void paint(Graphics g) {
   Graphics2D g2d = (Graphics2D) g;
   Image image = createImage(300, 200);
   Graphics2D g2dImage = (Graphics2D) image.getGraphics();

   DecoratedLines aDecoLines = null;

   if(freeCurveFlag) {
      aDecoLines = new DecoratedLines();

      for(int loopIndex = 0; loopIndex < ptIndex - 1; loopIndex++) {
         aDecoLines.addLine(new Line2D.Float(pts[loopIndex].x, pts[loopIndex].y, pts[loopIndex+1].x, pts[loopIndex+1].y));
      }
      aDecoLines.setColor(lineColor);
      aDecoLines.setStroke(drawStroke);
   }

   if (aDecoLines != null) {
      if (!mousePressed) {
         freeCurves.add(aDecoLines);
         ptIndex = 0;
      }
      else {
         aDecoLines.paintSelf(g2dImage);
      }
   }

   for (DecoratedLines lines : freeCurves) {
      lines.paintSelf(g2dImage);
   }

   g2d.drawImage(image, 0, 0, 300, 200, null);
}
--------------------------------------------------------

を下記のように書き換えましょう

--------------------------------------------------------
public void paint(Graphics g) {
   Graphics2D g2d = (Graphics2D) g;
   Image image = createImage(300, 200);
   Graphics2D g2dImage = (Graphics2D) image.getGraphics();

   DecoratedShape aShape = null;
   DecoratedLines aDecoLines = null;

   if(freeCurveFlag) {
      aDecoLines = new DecoratedLines();

      for(int loopIndex = 0; loopIndex < ptIndex - 1; loopIndex++) {
         aDecoLines.addLine(new Line2D.Float(pts[loopIndex].x, pts[loopIndex].y, pts[loopIndex+1].x, pts[loopIndex+1].y));
      }
      aDecoLines.setColor(lineColor);
      aDecoLines.setStroke(drawStroke);
   }
   else if(endPoint != null) {
      if (lineFlag) {
         aShape = new DecoratedLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
      }
      else {
         Point tempStartPoint;
         Point tempEndPoint;
         int drawWidth;
         int drawHeight;
         tempEndPoint = new Point(Math.max(endPoint.x, startPoint.x), Math.max(endPoint.y, startPoint.y));
         tempStartPoint = new Point(Math.min(endPoint.x, startPoint.x), Math.min(endPoint.y, startPoint.y));
         drawWidth = tempEndPoint.x - tempStartPoint.x;
         drawHeight = tempEndPoint.y - tempStartPoint.y;
         if(ellipseFlag) {
            aShape = new DecoratedEllipse(tempStartPoint.x, tempStartPoint.y, drawWidth, drawHeight);
         }
         else if(rectFlag) {
            aShape = new DecoratedRectangle(tempStartPoint.x, tempStartPoint.y, drawWidth, drawHeight);
         }
      }
      aShape.setColor(lineColor);
      aShape.setStroke(drawStroke);
   }

   if (aDecoLines != null) {
      if (!mousePressed) {
         freeCurves.add(aDecoLines);
         ptIndex = 0;
      }
      else {
         aDecoLines.paintSelf(g2dImage);
      }
   }
   if (aShape != null) {
      if (!mousePressed) {
         shapes.add(aShape);
      }
      else {
         aShape.paintSelf(g2dImage);
      }
   }

   for (DecoratedLines lines : freeCurves) {
      lines.paintSelf(g2dImage);
   }

   for (DecoratedShape shape : shapes) {
      shape.paintSelf(g2dImage);
   }

   g2d.drawImage(image, 0, 0, 300, 200, null);
}
--------------------------------------------------------

このソース・コードは、今までの知識があれば理解できるはずですので、
説明は省略します。
ただ、矩形や楕円を描くときは、始点が左上の隅になりますので、
startPointとendPointの位置関係がそれに合っていない場合は、始点と終点
の入れ替えを行って必ず始点が左上の隅になるようにしなければならない
ことに注意してください。
tempStartPointとtempEndPointはその入れ替え後の始点と終点を保管する
ための変数として用意しています。


(5) 下記のメソッドを追加しましょう。

--------------------------------------------------------
public void setFreeCurveFlag(boolean b) {
   freeCurveFlag = b;
}

public void setLineFlag(boolean b) {
   lineFlag = b;
}

public void setEllipseFlag(boolean b) {
   ellipseFlag = b;
}

public void setRectFlag(boolean b) {
   rectFlag = b;
}
--------------------------------------------------------

これらは、どの図形を描く状態であるかを示すフィールド(2)に値を設定変更
するためのsetterメソッドですね。



では、続いてGraffitiAppletの編集を行いましょう。
GraffitiAppletのVEエディターを開いてください。


描く図形を選択できるようにするためにJToggleButtonを4つ(自由曲線、直線、
矩形、楕円の4つ)貼り付けたいのですが、貼り付ける場所を確保するために
GraffitiAppletアプレットのサイズをもう少し大きくしておきましょう。

init()メソッドの中の下の行

--------------------------------------------------------
this.setSize(300, 200);
--------------------------------------------------------



--------------------------------------------------------
this.setSize(300, 250);
--------------------------------------------------------

に変更して保管しましょう。

GraffitiAppletのアプレットが下のほうに少し広がりましたね。ここに次の
ように4つのJToggleButtonを貼り付けて、そのtextプロパティーを変更して
ください。

(1) Bean Nameは「freeCurveToggleButton」、textプロパティーは「自由曲線」
(2) Bean Nameは「lineToggleButton」、textプロパティーは「直線」
(3) Bean Nameは「rectangleToggleButton」、textプロパティーは「矩形」
(4) Bean Nameは「ellipseToggleButton」、textプロパティーは「楕円」

ボタンの並び方が不揃いになっていたら、vol.056で色のボタンを整理したの
と同じようにしてこれらのボタンのレイアウトを調整し、きれいに整列する
ようにしましょう。幅や高さも最適なサイズに調整しましょう。


次にこれらのJToggleButtonを一つのButtonGroupにまとめて、ラジオボタン
のように働くようにしましょう。
init()メソッドの最後に以下のようなソース・コードを追加してください。

--------------------------------------------------------
ButtonGroup shapeGroup = new ButtonGroup();
shapeGroup.add(getFreeCurveToggleButton());
shapeGroup.add(getLineToggleButton());
shapeGroup.add(getRectangleToggleButton());
shapeGroup.add(getEllipseToggleButton());
--------------------------------------------------------


では次に、これらの各ボタンに対してリスナーのactionPerformed()メソッド
のプログラミングをしていきましょう。

例によって、「Java Beans」ビューの中でfreeCurveToggleButtonを右クリッ
クして、「Events」→「actionPerformed」を選択します。

するとgetFreeCurveToggleButton()メソッドに以下のソース・コードが自動的
に追加されますね。

--------------------------------------------------------
freeCurveToggleButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent e) {
      System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
   }
});
--------------------------------------------------------

これを以下のように編集しましょう。

--------------------------------------------------------
freeCurveToggleButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent e) {
      getGraffitiPanel().setFreeCurveFlag(true);
      getGraffitiPanel().setLineFlag(false);
      getGraffitiPanel().setRectFlag(false);
      getGraffitiPanel().setEllipseFlag(false);
   }
});
--------------------------------------------------------

このソース・コードが何をしているのかは、わかりますね。

同様にして、lineToggleButton、rectangleToggleButton、ellipseToggleButton
に対してもリスナーのactionPerformed()メソッドのソース・コードを自動生成
させ、それぞれ下記のように編集しましょう。


<getLineToggleButton()メソッドの中で>
--------------------------------------------------------
lineToggleButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent e) {
      getGraffitiPanel().setFreeCurveFlag(false);
      getGraffitiPanel().setLineFlag(true);
      getGraffitiPanel().setRectFlag(false);
      getGraffitiPanel().setEllipseFlag(false);
   }
});
--------------------------------------------------------


<getRectangleToggleButton()メソッドの中で>
--------------------------------------------------------
rectangleToggleButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent e) {
      getGraffitiPanel().setFreeCurveFlag(false);
      getGraffitiPanel().setLineFlag(false);
      getGraffitiPanel().setRectFlag(true);
      getGraffitiPanel().setEllipseFlag(false);
   }
});
--------------------------------------------------------


<getEllipseToggleButton()メソッドの中で>
--------------------------------------------------------
ellipseToggleButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent e) {
      getGraffitiPanel().setFreeCurveFlag(false);
      getGraffitiPanel().setLineFlag(false);
      getGraffitiPanel().setRectFlag(false);
      getGraffitiPanel().setEllipseFlag(true);
   }
});
--------------------------------------------------------

あと、アプレット起動時の最初は自由曲線が選択されている状態ですので、
「自由曲線」ボタンが選択されている状態にしておきましょう。
freeCurveToggleButtonの「selected」プロパティーの値をtrueに変更して
ください。

以上で、図形を選択して描く機能が完成しました。

保管してGraffitiAppletを起動してテストしてみてください。
うまく動きましたか。


ここでちょっと気になることがありますね。楕円を描くと線が何だかギザギザ
になっていてちょっと格好悪いですね。
直線も斜めの線はギザギザになりますね。

このギザギザを解消するためには、GraffitiPanelのpaint()メソッドの中の

--------------------------------------------------------
Graphics2D g2dImage = (Graphics2D) image.getGraphics();
--------------------------------------------------------

の行の下に

--------------------------------------------------------
g2dImage.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
--------------------------------------------------------

というコードを入れてください。
保管してテストすると線のギザギザが解消されることがわかりますね。
(ちなみにこのギザギザのことを「aliasing」と言い、「ANTIALIASING」は
「反ギザギザ」のような意味になります。)
なお、このコードを入れるとちょっと余計にCPUを消費するので、遅いCPUを
使っている人は遅さが目立つかもしれません。

このGraphics2DのsetRenderingHint()というメソッドは描画の品質をコント
ロールするためのメソッドなのですが、説明するのは面倒なので、とにかく
ギザギザを解消するためにはこのコードを使うことを覚えているだけでいい
です。詳しく知りたい人は自分でAPIリファレンスなどを読んで調べてくださ
い。

なお、このコードを入れても自由曲線のギザギザは解消されませんね。自由
曲線のほうは元々折れ線ですし、マウスの移動検出能力や操作にも依存しま
すので、線をなめらかにするには別の方法を使います(後述)。


ところで、GraffitiPanelのpaint()メソッドをよくみると、

--------------------------------------------------------
if (aDecoLines != null) {
   if (!mousePressed) {
      freeCurves.add(aDecoLines);
      ptIndex = 0;
   }
   else {
      aDecoLines.paintSelf(g2dImage);
   }
}
if (aShape != null) {
   if (!mousePressed) {
      shapes.add(aShape);
   }
   else {
      aShape.paintSelf(g2dImage);
   }
}

for (DecoratedLines lines : freeCurves) {
   lines.paintSelf(g2dImage);
}

for (DecoratedShape shape : shapes) {
   shape.paintSelf(g2dImage);
}
--------------------------------------------------------

のようにDecoratedLinesオブジェクトとDecoratedShapeオブジェクトで同じ
ような処理をしているところがあるのに気づきますね。ここらへんはもっと
簡素化できそうですね。

そもそもDecoratedLinesとDecoratedShapeは互いに同じようなメンバー(メ
ソッドとフィールド)を持っています。ということは、これらの共通のスー
パークラス(抽象クラス)を作って、共通なメンバーをスーパークラスに移
せば、上記のソース・コードもよりシンプルに変更できるということがわか
りますね。やる気のある人は、演習問題としてやってみてください。


今回はここまで。


(続く)


では、また来週。



========================================================
◆ 02.文法解説 [アプレット]
========================================================

[アプレット(3)「アプレットのパラメーター」]


アプレットの起動時にはパラメーターを指定することができます。

パラメーターの値は下記のようにAppletのgetParameter()メソッドを呼び
出すことによって取り出すことができます。

--------------------------------------------------------
String tempStr;
tempStr = getParameter("parametername");
--------------------------------------------------------

なお、パラメーターの値が設定されていないときは、getParameter()メソ
ッドはnullを返します。

これは例えば次のようにして使います。
パラメーターで与えられた幅、高さのデータを使って矩形を描くアプレット
を例に取ります。

--------------------------------------------------------
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;

public class FillRectApplet extends Applet {

   int width = 100;
   int height = 50;

   public void init() {
      String tempStr;
      tempStr = getParameter("rectwidth");
      if (tempStr != null) width = Integer.parseInt(tempStr);
      tempStr = getParameter("rectheight");
      if (tempStr != null) height = Integer.parseInt(tempStr);
   }

   public void paint(Graphics g) {
      g.setColor(new Color(255, 255, 0));
      g.fillRect(10, 10, width, height);
      g.setColor(new Color(0,255,0));
      g.drawRect(10, 10, width, height);
   }

}
--------------------------------------------------------

ここで、GraphicsのdrawRect()メソッドは矩形を描くメソッドで、Graphics
のfillRect()メソッドは矩形の中を塗りつぶすメソッドです。

(実際にHTMLでパラメーターを指定する方法については後述します。)

Eclipseでアプレットを起動するときにパラメーターを指定するには次のよう
にします。

(1) 上のアプレット(FillRectApplet)を作成して保管(コンパイル)して
おいたとします。

(2) パッケージ・エクスプローラーの中でFillRectAppletを右クリックし、
「実行」→「構成および実行」を選択します。

(3) 「構成および実行」ウインドウの中に「メイン」、「パラメーター」、
「引数」、・・・といったタブが並んでいるので、そのうちの「メイン」タブ
をクリックし、「プロジェクト」欄と「アプレット」欄の値が正しいことを
確認します。違っていたら、正しい値に変更してください。

(4) 「パラメーター」タブをクリックし、「追加」ボタンをクリックしてパラ
メーターの名前と値を追加していきます。一つ目のパラメーターは名前を
rectwidth(上のソース・コード内のgetParameter()メソッドに指定した引数
の文字列と一致させる)にし、値を20にしてみましょう。二つ目のパラメー
ターは名前をrectheightにし、値を10にしてみましょう。
(一度追加したパラメーターを修正したいときは、パラメーターを選択して
から「編集」ボタンをクリックして行います。またパラメーターを削除した
いときは、パラメーターを選択してから「除去」ボタンをクリックします。)

(5) 「実行」ボタンをクリックするとアプレット・ビューアが起動されて
アプレットが実行されます。


なお、「構成および実行」ウインドウは実行時の設定内容を記憶してしまい
ますので、その後の通常のアプレットの起動でも以前の設定内容で実行され
てしまいます。必要に応じて、「構成および実行」ウインドウの設定内容を
適時変更してください。


(続く)



================================================
◆ 03.任意演習問題
================================================

1.
DecoratedLinesとDecoratedShapeの共通のスーパークラス(抽象クラス)を
作って、共通なメンバーをスーパークラスに移し、上記のソース・コードを
よりシンプルに変更してください。

2.
インターフェースShapeを実装している図形クラスには直線や矩形や楕円以外
にもいくつかのものがあります。これらはともにGraphics2Dのdraw()メソッド
で描画することができます。
また、Graphics2Dの描画メソッドには直線や輪郭だけを描くdraw()メソッド
以外に、閉じた図形の内側まで塗りつぶすfill()メソッドというのもあります。
これらをAPIリファレンスで調べて、GraffitiAppletに機能を追加してみて
ください。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2007 Future Lifestyle Inc. 不許無断複製