如果想用 JavaFX快速开发富用户体验的应用,就好好读本文吧。我们将创建一个简单应用并了解到用少量代码实现复杂图形效果是多么容易。当然了,JavaFX不仅仅只是异常漂亮生动的形状而已。读完本文后去看看样例将大有好处。
Figure 1 Colorful Circles Application
Description of "Figure 1 Colorful Circles Application"
如果你很熟悉JavaFX场景图形编程模型,那就很容易理解程序的代码。 舞台( stage)是应用的最高层容器,场景( scene)是应用中内容的绘图表面。内容都被组织为场景图形,是一颗节点的层次树。
Figure 2 为应用 ColorfulCircles展示了场景图形。 分支节点是 Group 类的实例化。非分支节点,即叶子节点,是 Rectangle 和Circle 类的实例化。
Figure 2 Colorful Circles Scene Graph
Description of "Figure 2 Colorful Circles Scene Graph"
建立应用
你可以使用任何为开发Java设计的工具来构建javaFX应用。我们推荐使用NetBeans IDE。 开始之前,请确保你的NB版本已经支持了javaFX2.0。详见 System Requirements。
安装如下步骤用NB做开发:
- 从 File 菜单 , 选择New Project
- 从JavaFX application 分类中, 选择JavaFX Application , 点击Next
- 输入工程名 ColorfulCircles 后点 Finish
- 打开 ColorfulCircles.java 文件,复制 import 声明粘贴进你的工程覆盖NB自动生成的语句。
- 或者你可以使用NB的代码完成功能或Fix Imports 命令导入Import语句,不过记得确保包都以javafx开头。
创建应用基础
删除 NetBeans IDE生成的ColorfulCircles 类并用 Example 1 中的代码代替。以下是运行javaFX应用所需的最少代码:
Example 1 Basic Application
public class ColorfulCircles extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.show();
}
}
ColorfulCircles类继承了 Application 类,包含两个方法。第一个方法是 main() 方法, 用来调用 launch() 方法。作为JavaFX最佳实践,launch() 方法是被 main() 方法调用的唯一方法。
然后, ColorfulCircles 类重写了抽象的 start() 方法。start() 方法的参数是应用的初始舞台。最后一行使得舞台可见。
现在你可以编译运行ColorfulCircles了,每一步都记得查看下中间结果。如果出问题了,查看下 ColorfulCircles.java 文件。
添加场景
现在为舞台添加场景:增加三行代码,参见Example 2 。这有两个最佳实践: 代码中为场景创建了一个Group节点做根节点,并设置了场景的宽和高(这里是800和600)。
在 primaryStage.show() 这一行之前添加场景及其所有内容。这是另一个最佳实践。
Example 2 Scene
@Override
public void start(Stage primarystage) {
Group root = new Group();
Scene scene = new Scene(root, 800, 600, Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show()
Figure 3 展示了到目前为止的结果。
Figure 3 Stage and Scene
Description of "Figure 3 Stage and Scene"
添加图形
接下来在 primaryStage.show()行之前添加 Example 3 中的代码来创建30个圆。
Example 3 30 Circles
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);
这些代码创建了称为 circles 的Group,然后使用一个ofr循环向其中添加30个圆。每个圆半径到是150,并用white颜色填充,此外不透明度是5%,所以基本是透明的。
然后为这些圆创建边框,代码中包含了 StrokeType 类。 描边类型的 OUTSIDE 标明圆的边界向外扩展 StrokeWidth 的值,也就是4。描边的颜色是 white ,不透明度是16%,使得它比圆的颜色浅。
最后一行把 circles 加到根节点上。这只是临时结构,稍候将修改场景图形使得它匹配 Figure 2 展示那样。
Figure 4 展示了当前应用。由于代码没有为每个圆指定特定位置,它们都叠加在一起,并且窗口的左上角是圆心。层叠的圆的不透明度和黑色北京作用使得圆看起来是灰色的。
Figure 4 Circles
Description of "Figure 4 Circles"
增加视觉效果
继续为圆应用盒子模糊效果使得它们看起来柔和。代码是 Example 4 。 在primaryStage.show() 之前添加这些代码。
Example 4 Box Blur Effect
circles.setEffect(new BoxBlur(10, 10, 3));
代码设置了模糊半径,宽和高都是10并且迭代3次,使它接近高斯模糊。 这样便在圆的边缘出现了平滑效果,看Figure 5 .
Figure 5 Box Blur on Circles
Description of "Figure 5 Box Blur on Circles"
创建背景渐变
现在创建一个矩形并用线性渐变填充,代码见 Example 5 .
在 root.getChildren().add(circles) 之前添加这些代码,这样矩形才能在圆下面。
Example 5 Linear Gradient
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")),}));
root.getChildren().add(colors);
代码创建了称为colors的矩形。 矩形和场景同宽高,并从左下角的(0,0)点开始到右上角的(1,1)点应用线性渐变。 true 表示渐变在矩形中是成比例的,NO_CYCLE 表示颜色循环不会重复, Stop[] 序列表明了渐变颜色序列。最后一行把colors 添加到根节点。
现在边缘模糊的灰色圆出现在了彩虹色的上面,见 Figure 6 .
Figure 6 Linear Gradient
Description of "Figure 6 Linear Gradient"
Figure 7 展示了中间的场景图。现在circles 组和colors 矩形都是根节点的孩子。
Figure 7 Intermediate Scene Graph
Description of "Figure 7 Intermediate Scene Graph"
应用混合模式
现在通过增加混合覆盖效果给圆增加颜色并使场景变暗。这个任务需要一点家务活,你需要从场景中移除 circles组和渐变的矩形,并把它们添加到新的混合覆盖组中。
- 删除下面两行代码:root.getChildren().add(colors);root.getChildren().add(circles);
- 添加 Example 6 中的代码到上面删除代码的位置。 Example 6 Blend Mode 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 ,包含一个新建的匿名黑色矩形和以前创建的circles 组。第二个孩子是以前创建的colors 矩形。
- setBlendMode() 方法把混合覆盖应用到了colors 矩形。最后一行代码把blendModeGroup 添加到场景作为根节点的孩子,如Figure 2 .
混合覆盖效果是图形设计程序中的常规效果。它可以暗化图像或高亮它们,这取决于混合组中的颜色。这里,我们把线性渐变矩形用作覆盖,黑色矩形用来保持背景黑暗,而接近透明的圆从矩形中取了色不过依然变暗了。
Figure 8 展示了结果。下一步活化了圆之后将能看到完整的混合覆盖效果。
Figure 8 Overlay Blend
Description of "Figure 8 Overlay Blend"
添加动画
最后一步是使用javaFX动画来移动圆:
- 如果没准备好,增加 import static java.lang.Math.random; 到导入声明。
- 在 primaryStage.show() 之前增加Example 7 中的活化代码。
Example 7 Animation
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 循环为30个圆增加两个关键帧。第一个关键帧在0秒时使用translateXProperty 和translateYProperty 属性设置窗口内的一个随机位置。第二个关键帧在40秒时同样做。这样,当时间线play()后,所有圆就在40秒内从一个随机位置到另一个。
Figure 9 展示了运动中的30个圆。完成后查看 ColorfulCircles.java 文件 .
Figure 9 Animated Circles