1 使用JavaFX场景图
本教程探讨了JavaFX场景图应用程序编程接口(API),这是一个底层框架,用于将图形用户界面(GUI)渲染到屏幕上。
如果您是一位有经验的Java开发人员,那么很有可能您曾经创建过一个带有图形用户界面的应用程序。这可以是从网页中的小程序到桌面上的独立Swing应用程序。如果您曾经进行过任何自定义绘图,那么您应该熟悉Graphics
类及其相关方法。传统的方法虽然强大,但始终需要开发人员付出一定的努力来正确地将图形渲染到屏幕上。这项工作通常与应用程序的大部分逻辑分开。
概述
JavaFX场景图API使得创建图形用户界面更加容易,特别是涉及复杂的视觉效果和变换时。一个场景图是一种树形数据结构,最常见于图形应用程序和库,如矢量编辑工具、3D库和视频游戏。JavaFX场景图是一种保留模式API,意味着它在应用程序中维护了所有图形对象的内部模型。在任何给定的时间,它都知道要显示哪些对象,屏幕上哪些区域需要重绘,以及如何以最高效的方式渲染所有内容。您不需要直接调用原始绘图方法,而是使用场景图API,让系统自动处理渲染细节。这种方法显著减少了应用程序中所需的代码量。
JavaFX场景图中的每个单独的项目被称为节点。每个节点被分类为分支节点(表示它可以有子节点)或叶节点(表示它不能有子节点)。树中的第一个节点始终被称为根节点,它没有父节点。在图1-1中可以看到一个常见的继承图。
JavaFX API定义了许多可以充当根节点、分支节点或叶节点的类。当用实际的类名替换时,同样的图可能在实际应用中类似于图1-2。
在图1-2中,一个Group
对象充当根节点。Circle
和Rectangle
对象是叶节点,因为它们不(也不能)有子节点。Region
对象(定义了一个可以使用CSS样式化的子节点的屏幕区域)是一个包含两个叶节点(Text
和ImageView
)的分支节点。场景图可以比这个更大,但基本的组织方式——即父节点包含子节点的方式——在所有应用程序中都会重复。
探索API
那么这一切在代码方面意味着什么呢?让我们从设置一个基本的应用程序框架开始,只有根节点,如示例1-1所示。
示例1-1 创建应用程序框架
package scenegraphdemo; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 500, 500, Color.BLACK); stage.setTitle("JavaFX场景图演示"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
这段代码产生了一个窗口,看起来像图1-3所示。
需要考虑的重要点如下:
-
Main
类是javafx.application.Application
类的扩展。它的start
方法被重写,并接收一个Stage
对象(顶级GUI容器)作为唯一参数。 -
根节点(在本例中,是
javafx.scene.Group
类的实例)被创建并传递给场景的构造函数,同时还传递了场景的宽度、高度和填充颜色。 -
设置了舞台的标题、场景和可见性。
-
主方法调用了
Application.launch()
方法。
由于场景的填充颜色是黑色,所以最终的应用程序看起来是这样的。因为根节点目前没有子节点,所以没有其他内容可显示。通过在根节点中添加一个子节点,可以实现如示例1-2所示的修改。
示例1-2 添加一个叶节点
package scenegraphdemo; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 500, 500, Color.BLACK); Rectangle r = new Rectangle(25,25,250,250); r.setFill(Color.BLUE); root.getChildren().add(r); stage.setTitle("JavaFX场景图演示"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
通过在示例1-2中所示的更改,将出现一个250x250像素的蓝色矩形(叶节点),并位于指定的X和Y坐标上。(默认情况下,X从左到右增加,Y从上到下增加。但是,这可能会受到变换的影响。)图1-4显示了添加叶节点的结果。
由于图形对象由场景图管理,因此您可以通过很少的额外代码实现一些有趣的效果。例如,您可以轻松地使矩形在屏幕上来回弹跳,同时旋转,改变大小,并将其颜色从蓝色过渡到红色。
示例1-3使用过渡来实现这一点:
示例1-3 动画场景
package scenegraphdemo; import javafx.animation.FillTransition; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.animation.Timeline; import javafx.animation.ParallelTransition; import javafx.animation.RotateTransition; import javafx.animation.ScaleTransition; import javafx.animation.TranslateTransition; import javafx.util.Duration; public class Main extends Application { @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 500, 500, Color.BLACK); Rectangle r = new Rectangle(0, 0, 250, 250); r.setFill(Color.BLUE); root.getChildren().add(r); TranslateTransition translate = new TranslateTransition(Duration.millis(750)); translate.setToX(390); translate.setToY(390); FillTransition fill = new FillTransition(Duration.millis(750)); fill.setToValue(Color.RED); RotateTransition rotate = new RotateTransition(Duration.millis(750)); rotate.setToAngle(360); ScaleTransition scale = new ScaleTransition(Duration.millis(750)); scale.setToX(0.1); scale.setToY(0.1); ParallelTransition transition = new ParallelTransition(r, translate, fill, rotate, scale); transition.setCycleCount(Timeline.INDEFINITE); transition.setAutoReverse(true); transition.play(); stage.setTitle("JavaFX场景图演示"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
虽然这些示例可能很简单,但它们识别并演示了您在大多数图形应用程序中将使用的一些重要概念。
javafx.scene
包定义了十几个类,但其中三个在学习API结构时最重要:
-
Node
:所有场景图节点的抽象基类。 -
Parent
:所有分支节点的抽象基类。(此类直接扩展自Node
)。 -
Scene
:场景图中所有内容的基本容器类。
这些基类定义了重要的功能,随后将由子类继承,包括绘制顺序、可见性、变换组合、支持CSS样式等。您还会发现各种直接从Parent
类继承的分支节点类,例如Control
、Group
、Region
和WebView
。叶节点类定义在其他一些附加包中,例如javafx.scene.shape
和javafx.scene.text
。