你可以使用JavaFX很快的开发应用并且给用户带来丰富的用户体验。在这一个小节,我们使用非常少的代码创建一个动态且有复杂效果的
应用:五彩缤纷的圆圈,如下图所示:
下面这个图显示了场景图形,有分支的节点代表了Group类的实例,没有分支也就是叶子节点代表了Rectangle和Circle类的实例:
建立一个JavaFX项目
建立一个java文件,命名ColorfulCircles ,代码如下所示:
public class ColorfulCircles extends Application { @Override public void start(Stage primaryStage) { Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
这个应用比较合适实用group作为场景的根节点。group的大小取决于里面节点的大小。然而,通常我们希望我们场景的大小会跟随舞台(
窗口)的大小改变而改变,如果是这样的话,我们可以使用可变大小的布局节点,就上之前文章中登陆表单那样。
你现在已经可以运行这个应用了,你应该在途中经常时不时的看一下结果。如果运行出错了,可以停下看看一下代码。现在运行的结果是
一个黑色的窗口。
下面,我们添加30个圆圈
Group circles = new Group(); for (int i = 0; i < 30; i++) { Circle circle = new Circle(150, Color.web("white", 0.05)); circle.setStrokeType(StrokeType.OUTSIDE); circle.setStroke(Color.web("white", 0.16)); circle.setStrokeWidth(4); circles.getChildren().add(circle); } root.getChildren().add(circles);
这段代码创建了一个Group,名字是circles,然后用一个循环添加了30个圆圈到这个group,每个圆圈的半径是150,颜色是白色,不透明
度是5%,这意味着这个圆圈几乎是透明的。
然后为这些圆圈创建一个边框,使用stroke,这里是边框为 外边框,白色 ,不透明度为16%,宽度是4.所以这个边框比圆圈应该更明亮。
最后把这些圆圈添加到root节点里,这只是一个临时的结构,之后我们会修改场景图形是它表现的和上面说的一致。
运行应用,如下图所示:
为圆圈加上盒子模糊效果
circles.setEffect(new BoxBlur(10, 10, 3));
这段代码设置模糊半径10像素宽 和 10像素高,模糊迭代3,是它接近于高斯模糊,这样会使得圆的边缘过度更加平滑。运行代码如下图所
示:
创建一个矩形,设置它为线性渐变,代码如下:
Rectangle colors = new Rectangle(scene.getWidth(), scene.getHeight(), new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, new Stop[]{ new Stop(0, Color.web("#f8bd55")), new Stop(0.14, Color.web("#c0fe56")), new Stop(0.28, Color.web("#5dfbc1")), new Stop(0.43, Color.web("#64c2f8")), new Stop(0.57, Color.web("#be4af7")), new Stop(0.71, Color.web("#ed5fc2")), new Stop(0.85, Color.web("#ef504c")), new Stop(1, Color.web("#f2660f")),})); colors.widthProperty().bind(scene.widthProperty()); colors.heightProperty().bind(scene.heightProperty()); root.getChildren().add(colors);
这段代码创建了一个矩形,命名为colores。这个矩形的大小和场景的大小一致,开始于左上角(0,1),结束于右上角(1,0),true表示
线性渐变保持比例,NO_CYCLE表示渐变不会重复,Stop[]序列表示在特定的地方渐变。
接下来的两行代码,使得渐变会跟随窗口的变化而变化。最后把这个颜色的渐变添加到root节点:
运行应用如下图所示:
目前场景图形如下图所示:
接下来,我们为圆圈添加颜色,并且通过一个叠加混合效果使场景变暗,
1:找到下面的代码:
root.getChildren().add(colors); root.getChildren().add(circles);
2:用下面的代码替换第一步中的代码:
Group blendModeGroup = new Group(new Group(new Rectangle(scene.getWidth(), scene.getHeight(), Color.BLACK), circles), colors); colors.setBlendMode(BlendMode.OVERLAY); root.getChildren().add(blendModeGroup);
blendModeGroup的架构是叠加混合,这个group有两个子节点,第一个是新的Group,这个group是一个黑色的矩形和之前创建的圆圈group
,第二个是之前创建的颜色矩形,
setBlendMode方法使得叠加混合应用于颜色矩形最后把这个group添加到场景中。
运行应用,如下图所示:
最后的步骤就是使用JavaFX动画移动这些圆圈
添加如下代码:
Timeline timeline = new Timeline(); for (Node circle: circles.getChildren()) { timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, // set start position at 0 new KeyValue(circle.translateXProperty(), random() * 800), new KeyValue(circle.translateYProperty(), random() * 600) ), new KeyFrame(new Duration(40000), // set end position at 40s new KeyValue(circle.translateXProperty(), random() * 800), new KeyValue(circle.translateYProperty(), random() * 600) ) ); } // play 40s of animation timeline.play();
动画是靠时间轴来驱动的,所以这段代码里创建了一个时间轴,然后使用for循环为每个圆圈添加了两个关键帧。第一个关键帧在0秒的时
候使用translateXProperty 和 translateYProperty 属性确定在窗口中的一个随机的位置。第二个关键帧在40秒后做相同的事情。所以当
时间轴开始后,每个圆圈都会在40秒内从一个位置移动到另一个位置。
运行应用如下图所示:
全部代码如下所示:
package com.chu.helloworld; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.effect.BlendMode; import javafx.scene.effect.BoxBlur; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.scene.shape.StrokeType; import javafx.stage.Stage; import javafx.util.Duration; public class ColorfulCircles extends Application { @Override public void start(Stage primaryStage) { Group root = new Group(); Group circles = new Group(); for (int i = 0; i < 30; i++) { Circle circle = new Circle(150, Color.web("white", 0.05)); circle.setStrokeType(StrokeType.OUTSIDE); circle.setStroke(Color.web("white", 0.16)); circle.setStrokeWidth(4); circles.getChildren().add(circle); } circles.setEffect(new BoxBlur(10, 10, 3)); Scene scene = new Scene(root, 800, 600, Color.BLACK); primaryStage.setScene(scene); Rectangle colors = new Rectangle(scene.getWidth(), scene.getHeight(), new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, new Stop[] { new Stop(0, Color.web("#f8bd55")), new Stop(0.14, Color.web("#c0fe56")), new Stop(0.28, Color.web("#5dfbc1")), new Stop(0.43, Color.web("#64c2f8")), new Stop(0.57, Color.web("#be4af7")), new Stop(0.71, Color.web("#ed5fc2")), new Stop(0.85, Color.web("#ef504c")), new Stop(1, Color.web("#f2660f")), })); colors.widthProperty().bind(scene.widthProperty()); colors.heightProperty().bind(scene.heightProperty()); Group blendModeGroup = new Group(new Group(new Rectangle( scene.getWidth(), scene.getHeight(), Color.BLACK), circles), colors); colors.setBlendMode(BlendMode.OVERLAY); root.getChildren().add(blendModeGroup); Timeline timeline = new Timeline(); for (Node circle: circles.getChildren()) { timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, // set start position at 0 new KeyValue(circle.translateXProperty(), Math.random() * 800), new KeyValue(circle.translateYProperty(), Math.random() * 600) ), new KeyFrame(new Duration(40000), // set end position at 40s new KeyValue(circle.translateXProperty(), Math.random() * 800), new KeyValue(circle.translateYProperty(), Math.random() * 600) ) ); } // play 40s of animation timeline.play(); primaryStage.show(); } public static void main(String[] args) { launch(args); } }