9 Canvas APIの使用
この章では、JavaFX Canvas APIについて説明し、コンパイルして実行できるコード例を示します。 グラフィックス・チュートリアルのソース・コードページにあるリンクを使用して、NetBeans IDEプロジェクトの例をダウンロードします。
概要
JavaFX Canvas APIによって、書込み可能なカスタム・テクスチャが提供されます。 これは、javafx.scene.canvasパッケージ内のクラスCanvasおよびGraphicsContextによって定義されています。 このAPIの使用には、Canvasオブジェクトの作成、そのGraphicsContextの取得、および画面にカスタム図形をレンダリングするための描画操作の起動が含まれます。 CanvasはNodeサブクラスであるため、JavaFX Sceneグラフで使用できます。
基本図形の描画
BasicOpsTestプロジェクト(図9-1を参照)によって、Canvasが作成され、そのGraphicsContextが取得され、基本図形が描画されます。 GraphicsContextクラスのメソッドを使用すると、線、楕円、角丸四角形、円弧および多角形をすべて使用できます。 完全なBasicOpsTest NetBeansプロジェクトについては、BasicOpsTest.zipファイルをダウンロードしてください。
例9-1 キャンバスでの基本図形の描画
package canvastest;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.stage.Stage;
public class BasicOpsTest extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Operations Test");
Group root = new Group();
Canvas canvas = new Canvas(300, 250);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.setFill(Color.GREEN);
gc.setStroke(Color.BLUE);
gc.setLineWidth(5);
gc.strokeLine(40, 10, 10, 40);
gc.fillOval(10, 60, 30, 30);
gc.strokeOval(60, 60, 30, 30);
gc.fillRoundRect(110, 60, 30, 30, 10, 10);
gc.strokeRoundRect(160, 60, 30, 30, 10, 10);
gc.fillArc(10, 110, 30, 30, 45, 240, ArcType.OPEN);
gc.fillArc(60, 110, 30, 30, 45, 240, ArcType.CHORD);
gc.fillArc(110, 110, 30, 30, 45, 240, ArcType.ROUND);
gc.strokeArc(10, 160, 30, 30, 45, 240, ArcType.OPEN);
gc.strokeArc(60, 160, 30, 30, 45, 240, ArcType.CHORD);
gc.strokeArc(110, 160, 30, 30, 45, 240, ArcType.ROUND);
gc.fillPolygon(new double[]{10, 40, 10, 40},
new double[]{210, 210, 240, 240}, 4);
gc.strokePolygon(new double[]{60, 90, 60, 90},
new double[]{210, 210, 240, 240}, 4);
gc.strokePolyline(new double[]{110, 140, 110, 140},
new double[]{210, 210, 240, 240}, 4);
}
}
例9-1に示すように、Canvasは幅300および高さ250でインスタンス化されます。 次に、そのGraphicsContextが、canvas.getGraphicsContext2D()のコールによって取得されます。 その後、strokeLine、fillOval、strokeArc、fillPolygonなどのメソッドを呼び出すことによって、多数の基本描画操作が実行されます。
グラデーションおよびシャドウの適用
次の例(CanvasTestプロジェクト)では、カスタム図形をグラデーションおよびシャドウとともに描画することによって、GraphicsContextのメソッドをさらにテストします。 最終結果は、図9-2に示すようになります。 完全なCanvasTest NetBeansプロジェクトについては、CanvasTest.zipファイルをダウンロードしてください。
この例のコードは、各描画操作が固有のprivateメソッドで実行されるように構成されています。 これにより、対象となるメソッドを呼び出す(またはコメント・アウト)だけで、様々な機能をテストできます。 Canvas APIの学習という点では、重点を置くコードは、基礎となるCanvasまたはGraphicsContextオブジェクトのコールであることに注意してください。
このパターンには、5つの主要な部分があります。
最初に、Canvasの位置が座標(0,0)で設定されます。 これは、平行移動変換を基礎となるCanvasオブジェクトに適用する、例9-2のコードを呼び出すことで行われます。
例9-2 キャンバスの移動
private void moveCanvas(int x, int y) {
canvas.setTranslateX(x);
canvas.setTranslateY(y);
}
その他の値をパラメータとして渡して、Canvasを新しい位置に移動できます。 渡した値はsetTranslateXおよびsetTranslateYメソッドに転送され、Canvasはそれに応じて移動します。
次に、(大文字のDのような)最初の図形が画面に描画されます。 これは、GraphicsContecxtオブジェクトのbezierCurveToメソッドで呼び出されるベジエ曲線で行われます。
例9-3 画面でのベジエ曲線(大文字のD)の描画
private void drawDShape() {
gc.beginPath();
gc.moveTo(50, 50);
gc.bezierCurveTo(150, 20, 150, 150, 75, 150);
gc.closePath();
}
パラメータ値を変更することによって、この図形を試すことができます。 bezierCurveToでは、拡大したり引っ張ると図形がそれに応じて拡大または引っ張られます。
その後で、赤および黄のRadialGradientによって、背景に表示される円形パターンが指定されます。
例9-4 RadialGradientの描画
private void drawRadialGradient(Color firstColor, Color lastColor) {
gc.setFill(new RadialGradient(0, 0, 0.5, 0.5, 0.1, true,
CycleMethod.REFLECT,
new Stop(0.0, firstColor),
new Stop(1.0, lastColor)));
gc.fill();
}
ここで、GraphicsContextのsetFillメソッドは、そのパラメータとしてRadialGradientオブジェクトを受け入れます。 再度、異なる値を試すか、異なる色を渡すことができます。
LinearGradientでは、カスタムD図形に青から緑の色が付けられます。
例9-5 LinearGradientの描画
private void drawLinearGradient(Color firstColor, Color secondColor) {
LinearGradient lg = new LinearGradient(0, 0, 1, 1, true,
CycleMethod.REFLECT,
new Stop(0.0, firstColor),
new Stop(1.0, secondColor));
gc.setStroke(lg);
gc.setLineWidth(20);
gc.stroke();
}
このコードでは、GraphicsContextの線はLinearGradientを使用するように設定され、パターンはgc.stroke()を使用してレンダリングされます。
最後に、GraphicContextオブジェクトでapplyEffectを呼び出して、多色のドロップ・シャドウが指定されます。
例9-6 DropShadowの追加
private void drawDropShadow(Color firstColor, Color secondColor,
Color thirdColor, Color fourthColor) {
gc.applyEffect(new DropShadow(20, 20, 0, firstColor));
gc.applyEffect(new DropShadow(20, 0, 20, secondColor));
gc.applyEffect(new DropShadow(20, -20, 0, thirdColor));
gc.applyEffect(new DropShadow(20, 0, -20, fourthColor));
}
例9-6に示すように、この効果を適用するには、指定の色を使用してDropShadowオブジェクトを作成し、このオブジェクトをGraphicsContextオブジェクトのapplyEffectメソッドに渡します。
ユーザーとの対話
次のデモ(プロジェクトCanvasDoodleTest)では、青い正方形が画面に表示され、ユーザーがその表面でマウスをドラッグするとゆっくり消されていきます。 完全なCanvasDoodleTest NetBeansプロジェクトについては、CanvasDoodleTest.zipファイルをダウンロードしてください。
基本図形およびグラデーションの作成方法について説明しました。例9-7のコードでは、ユーザーとの対話部分にのみ焦点を当てています。
例9-7 ユーザーとの対話
...
private void reset(Canvas canvas, Color color) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(color);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
@Override
public void start(Stage primaryStage) {
...
final GraphicsContext gc = canvas.getGraphicsContext2D();
...
// Clear away portions as the user drags the mouse
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
gc.clearRect(e.getX() - 2, e.getY() - 2, 5, 5);
}
});
// Fill the Canvas with a Blue rectnagle when the user double-clicks
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
if (t.getClickCount() >1) {
reset(canvas, Color.BLUE);
}
}
});
...
例9-7では、四角形全体をデフォルトの青色で塗りつぶすresetメソッドを定義します。 しかし、最も興味深いコードは、ユーザーとの対話のためにオーバーライドされるstartメソッド内にあります。 最初のコメントがあるセクションでは、ユーザーがマウスをドラッグしたときにMouseEventオブジェクトを処理するイベント・ハンドラが追加されます。 ドラッグのたびに、GraphicsContextオブジェクトのclearRectメソッドが呼び出され、現在のマウス座標および消去される領域のサイズが渡されます。 これが発生すると、図9-4に示すように、背景のグラデーションが透けて表示されます。
残りのコードでは、単にクリック数をカウントし、ユーザーがマウスをダブルクリックした場合に青い正方形を元の状態にリセットします。
単純なレイヤー・システムの作成
複数のCanvasオブジェクトをインスタンス化し、これらを使用して単純なレイヤー・システムを定義することもできます。 したがって、レイヤーの切替えでは、目的のCanvasの選択とそこへの書込みが問題になります。 (Canvasオブジェクトは完全に透明であり、その一部の上で描画するまでは透けて表示されます。)
この最後のデモ(LayerTestプロジェクト)では、2つのCanvasオブジェクトを追加および作成し、直接重ね合せて配置することで、そのようなシステムが定義されます。 画面をクリックすると、現在選択されているレイヤー上に色付きの円が表示されます。 画面の上部でChoiceBoxを使用することで、レイヤーを変更できます。 レイヤー1に追加される円は緑です。 レイヤー2に追加される円は青です。 完全なLayerTest NetBeansプロジェクトについては、LayerTest.zipファイルをダウンロードしてください、
このデモのGUIでは、コンポーネントを管理するためにBorderPaneが使用されます。 ChoiceBoxが上部に追加され、Panelは2つのCanvasオブジェクトが追加されてから画面の中央に追加されます。
例9-8 レイヤーの作成および追加
...
private void createLayers(){
// Layers 1&2 are the same size
layer1 = new Canvas(300,250);
layer2 = new Canvas(300,250);
// Obtain Graphics Contexts
gc1 = layer1.getGraphicsContext2D();
gc1.setFill(Color.GREEN);
gc1.fillOval(50,50,20,20);
gc2 = layer2.getGraphicsContext2D();
gc2.setFill(Color.BLUE);
gc2.fillOval(100,100,20,20);
}
...
private void addLayers(){
// Add Layers
borderPane.setTop(cb);
Pane pane = new Pane();
pane.getChildren().add(layer1);
pane.getChildren().add(layer2);
layer1.toFront();
borderPane.setCenter(pane);
root.getChildren().add(borderPane);
}
...
ユーザーとの対話は、各レイヤーにイベント・ハンドラを直接追加することによって達成できます。 CanvasをクリックするとMouseEventが生成され、これが受け取られると現在のマウスの位置に円が描画されます。
例9-9 イベント・ハンドラの追加
private void handleLayers(){
// Handler for Layer 1
layer1.addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
gc1.fillOval(e.getX(),e.getY(),20,20);
}
});
// Handler for Layer 2
layer2.addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
gc2.fillOval(e.getX(),e.getY(),20,20);
}
});
}
両方のレイヤーが直接重ね合せて配置されているため、最前面のCanvasのみでマウス・クリックが処理されます。 特定のレイヤーを重なりの前面に移動するには、単純に、画面の上部にあるChoiceBoxコンポーネントから選択します。
例9-10 レイヤーの選択
private void createChoiceBox(){
cb = new ChoiceBox();
cb.setItems(FXCollections.observableArrayList(
"Layer 1 is GREEN", "Layer 2 is BLUE"));
cb.getSelectionModel().selectedItemProperty().
addListener(new ChangeListener(){
@Override
public void changed(ObservableValue o, Object o1, Object o2){
if(o2.toString().equals("Layer 1 is GREEN")){
layer1.toFront();
}else if(o2.toString().equals("Layer 2 is BLUE")){
layer2.toFront();
}
}
});
cb.setValue("Layer 1 is GREEN");
}
例9-10に示すように、ChangeListenerがChoiceBoxで登録され、toFront()を該当するCanvasで呼び出すことで、選択したレイヤーが最前面に移動されます。 レイヤーの選択は、青および緑の円を多数追加した後でレイヤーを切り替えるとよりわかりやすくなります。 (円の端を見ることで)どちらのレイヤーが前面に移動されているかがわかります。 図9-6および図9-7に、どのような表示になるかを示します。
レイヤーを選択する機能は、イメージ操作プログラムなどのソフトウェア・アプリケーションで一般的です。 また、各CanvasオブジェクトはNodeであるため、他のコンポーネントで適用するすべての標準的な変換およびビジュアル効果を自由に適用できます。








