绘制基本形状
在BasicOpsTest项目中(如图9-1所示),创建一个Canvas,获取其GraphicsContext,并在其上绘制一些基本形状。使用GraphicsContext类的方法可以绘制线条、椭圆、圆角矩形、弧形和多边形。下载完整的BasicOpsTest NetBeans项目的BasicOpsTest.zip文件。
示例9-1 在Canvas上绘制一些基本形状
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("绘制操作测试");
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。然后通过调用canvas.getGraphicsContext2D()获取其GraphicsContext。之后,通过调用strokeLine、fillOval、strokeArc和fillPolygon等方法进行一些基本的绘图操作。
您可以通过更改参数值来尝试此形状。当您这样做时,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文件
您已经了解了如何创建基本形状和渐变,因此Example 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();
...
// 当用户拖动鼠标时,清除部分内容
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
gc.clearRect(e.getX() - 2, e.getY() - 2, 5, 5);
}
});
// 当用户双击时,用蓝色矩形填充画布
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
if (t.getClickCount() >1) {
reset(canvas, Color.BLUE);
}
}
});
...
Example 9-7定义了一个reset方法,用默认的蓝色填充整个矩形。但最有趣的代码出现在被重写的start方法中,用于与用户交互。第一个被注释的部分添加了一个事件处理程序,以处理用户拖动鼠标时的MouseEvent对象。每次拖动时,调用GraphicsContext对象的clearRect方法,传入当前鼠标坐标以及要清除的区域的大小。在此过程中,背景渐变将显示出来,如Figure 9-4所示。
剩余的代码只是计算点击次数,并在用户双击鼠标时将蓝色正方形重置为初始状态。
因为两个图层直接放在一起,只有最上面的Canvas会处理鼠标点击事件。要将特定的图层移到堆栈的前面,只需从屏幕顶部的ChoiceBox组件中选择它。
示例9-10 选择图层
private void createChoiceBox(){
cb = new ChoiceBox();
cb.setItems(FXCollections.observableArrayList(
"图层1是绿色", "图层2是蓝色"));
cb.getSelectionModel().selectedItemProperty().
addListener(new ChangeListener(){
@Override
public void changed(ObservableValue o, Object o1, Object o2){
if(o2.toString().equals("图层1是绿色")){
layer1.toFront();
}else if(o2.toString().equals("图层2是蓝色")){
layer2.toFront();
}
}
});
cb.setValue("图层1是绿色");
}
如示例9-10所示,ChoiceBox上注册了一个ChangleListener,通过在适当的Canvas上调用toFront()将选定的图层置于前景。在添加了许多蓝色和绿色圆圈后,通过切换图层,您将能够通过观察圆圈边缘来判断哪个图层已经移到前面。 图9-6和图9-7展示了这个效果。
选择图层的能力在软件应用程序中很常见,例如图像处理程序。而且,由于每个Canvas对象都是一个Node,您可以自由地应用在其他组件上的所有标准变换和视觉效果。

