javaFX初探(使用画布 API)
本章介绍javaFX画布API的使用。
概述
javaFX 画布API提供了自定义的纹理。画布API有两个主要的类,Canvas 和 GraphicsContext,定义在javafx.scene.canvas包下。我们可
以创建一个Canvas对象,燃火获得它的GraphicsContext,然后渲染我们自定义的形状。因为Canvas是Node的子类,所以我们可以在场景图
中使用。
基本形状:
下面这个例子使用 Canvas和GraphicesContext来画出圆,矩形,多边形等。代码如下:
package com.chu.canvas;
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);
}
}
运行如下图所示:
]
使用渐变和阴影]
这个例子使用了更多的GraphicsContext的方法,比如:strokeLine, fillOval, strokeArc, 和fillPolygon
代码如下:
package com.chu.canvas;
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.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.stage.Stage;
public class CanvasTest extends Application {
private Canvas canvas = new Canvas(200, 200);
private GraphicsContext gc = canvas.getGraphicsContext2D();
private Group root = new Group();
@Override
public void start(Stage primaryStage) {
// setUserAgentStylesheet(STYLESHEET_MODENA);
primaryStage.setTitle("Canvas Test");
moveCanvas(0,0);
drawDShape();
drawRadialGradient(Color.RED, Color.YELLOW);
drawLinearGradient(Color.BLUE, Color.GREEN);
drawDropShadow(Color.GRAY, Color.BLUE, Color.GREEN, Color.RED);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
/**
* Moves the canvas to a new location within the Scene. This is accomplished
* by performing a translation transformation on the Canvas object, passing
* in the desired x and y coordinates. Passing in values of 0,0 will position
* the Canvas in the upper left corner of the Scene.
* @param x The new x coordinate
* @param y The new y coordinate
*/
private void moveCanvas(int x, int y) {
canvas.setTranslateX(x);
canvas.setTranslateY(y);
}
/**
* Draws an area in the shape of a capital letter "D."
* The user can try substituting numbers
* of their own in the bezierCurveTo parameters to
* warp the shape away from the letter "D."
*/
private void drawDShape() {
gc.beginPath();
gc.moveTo(50, 50);
gc.bezierCurveTo(150, 20, 150, 150, 75, 150);
gc.closePath();
}
/**
* Draws a radial gradient on the Canvas object, which appears as a series of
* circles radiating outward. This demo uses RED and YELLOW by default.
*
* @param firstColor The color used in the first Stop of the gradient.
* @param lastColor The color used in the last Stop of the gradient.
*/
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();
}
/**
* Draws a linear gradient on the Canvas object, which colors the letter "D"
* from top to bottom. The default colors used in this demo are BLUE and GREEN.
*
* @param firstColor
* @param secondColor
*/
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();
}
/**
* Draws a four separate drop shadows around the letter "D." The default
* colors used in the demo are GREY, BLUE, GREEN, and RED.
*
* @param firstColor
* @param secondColor
* @param thirdColor
* @param fourthColor
*/
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));
}
public static void main(String[] args) {
launch(args);
}
}
运行如下图所示:
用户交互
下面的例子,显示了一个蓝色的矩形,用户拖动鼠标可以抹去蓝色,双击可以还原。
代码如下:
package com.chu.canvas;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class CanvasDoodleTest extends Application {
/**
* Resets the canvas to its original look by filling in a rectangle covering
* its entire width and height. Color.BLUE is used in this demo.
*
* @param canvas The canvas to reset
* @param color The color to fill
*/
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) {
primaryStage.setTitle("Canvas Doodle Test");
Group root = new Group();
// Draw background with gradient
Rectangle rect = new Rectangle(400, 400);
drawBackground(rect);
root.getChildren().add(rect);
// Create the Canvas, filled in with Blue
final Canvas canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
reset(canvas, Color.BLUE);
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);
}
}
});
// Add the Canvas to the Scene, and show the Stage
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
/**
* Draws the background with a RadialGradient
* that transitions from Red to Yellow.
* @param rect the Rectangle to draw on the Canvas
*/
private void drawBackground(Rectangle rect) {
rect.setFill(new LinearGradient(0, 0, 1, 1, true,
CycleMethod.REFLECT,
new Stop(0, Color.RED),
new Stop(1, Color.YELLOW)));
}
public static void main(String[] args) {
launch(args);
}
}
运行如下图所示:
创建一个简单的系统层
我们可以创建多个Canvas对象,使用这个对象定义一个系统层。改变层后可以画不同的东西在画布上。
代码如下:
package com.chu.canvas;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.control.ChoiceBox;
import javafx.collections.FXCollections;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
public class LayerTest extends Application {
private Group root;
private BorderPane borderPane;
private Canvas layer1;
private Canvas layer2;
private GraphicsContext gc1;
private GraphicsContext gc2;
private ChoiceBox cb;
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 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);
}
});
}
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");
}
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);
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Build GUI
borderPane = new BorderPane();
primaryStage.setTitle("Layer Test");
root = new Group();
createLayers();
handleLayers();
createChoiceBox();
addLayers();
// Show Scene
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
运行代码如下图所示: