你可以使用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);
}
}