javafx2开发教程

bagger2016 贡献于2016-05-31

作者 Lawnstein.Chan  创建于2012-05-14 06:21:00   修改者huangtao  修改于2013-08-29 01:06:00字数243197

文档摘要:javaFX2.0是JavaFX最新的主要升级发布版。它的很多新特性并不能和1.3版本兼容。如果想要用javaFX做开发,推荐使用2.0版本。建立SDK开始使用javafx的第一步就是在你的电脑上下载和安装javafxSDK。你可以安装完整版SDK,也可以只安装javaFX运行时环境。
关键词:

第一回 JavaFX2.0简介 javaFX2.0是JavaFX最新的主要升级发布版。它的很多新特性并不能和1.3版本兼容。如果想要用javaFX做开发,推荐使用2.0版本。 建立SDK 开始使用javafx的第一步就是在你的电脑上下载和安装javafx SDK。你可以安装完整版SDK,也可以只安装javaFX运行时环境。详见第二回。 创建应用程序框架 每个JavaFX应用都有相似的框架。程序的主类继承了Application类,main方法调用luanch方法,start方法建立并显示舞台(stage,包括了应用的UI)。 定义UI 应用接口的舞台包括了一个或多个场景(scene,依次包含了控件、形状、图形来构建接口。) 布局UI控件 决定了在UI中使用什么控件后,使用内置的布局面板来管理控件的大小和位置。 部署 JavaFX应用运行在桌面、浏览器和网络上。完成应用后,将它们做成jar、html、jnlp文件供用户使用。 第二回 安装JavaFX2.0 SDK 通过本文,你讲学会如何在windows系统中安装JavaFX2.0。你有两个选择: 安装JavaFX SDK, 包括JavaFX运行时 仅安装JavaFX运行时 同时本文还指导了如何运行样例程序和怎样搭建JavaFX的NB环境。 Mac系统也有自己的 JavaFX 2.0开发者预览版,详见  第三回. 安装JavaFX SDK 如果你想要用IDE或者命令行开发javafx应用,请安装JavaFX SDK。SDK包括了Javafx的类库和工程文件。 如何安装JavaFX SDK: 检查你的系统要求 到  http://www.oracle.com/technetwork/java/javafx/downloads/index.html找到Javafx sdk针对你系统的版本下载保存。 运行.exe文件安装。 如何卸载JavaFX SDK: 使用标准卸载程序进行卸载。 安装JavaFX Runtime 若你想在你桌面上、浏览器中或使用Java Web Start通过网络运行Javafx应用,可以只安装运行时。 注意; 如果你已经成功安装了JavaFX SDK,就无需再单独安装运行时了。 如何安装JavaFX Runtime: 检查系统要求 找到并下载你系统对应的版本。 运行.exe文件。 如何卸载JavaFX Runtime 使用标准卸载程序进行卸载 运行样例程序 下载javafx样例程序,里面有一系列用javafx构建的应用。要运行它们,必须安装好运行时。 到  http://www.oracle.com/technetwork/java/javafx/downloads/index.html.点击JavaFX 2.0 Samples的链接,保存javafx_samples-2_0.zip到本地. 从zip文件中解压样例文件到本地目录。 进入目录。 双击其中任意一个可执行文件即可运行。比如,要在桌面运行Ensemble应用,双击Ensemble.jar。要在浏览器中运行,双击html。要通过网络运行,打开Ensemble.jnlp。 每个样例的源代码都在javafx-samples-2.0\src目录下。想要查看源代码,到javafx-samples-2.0\src\sample目录下,里面有你感兴趣的应用。每个样例的源代码都是NB工程。 搭建JavaFX的NetBeans环境 按照第四回搭建NB环境。 第三回 JavaFX2.0在mac系统的安装 JavaFX 2.0平台预览版包括了JavaFX SDK, 它提供了开发应用的工具和技术。 这次发布为JavaFX提供了Java API, 这为Java开发者提供了开放的功能,使得大量Java开发工具能用来开发 javafx应用。此外,本次发布还提供了: 高性能图像引擎,为制作简易、平滑、快捷的富图像提供了高级支持。 媒体引擎,支持网络多媒体内容后台运行。 网络组件,是HTML能绑定在javafx应用中。 UI控件的扩展集,譬如Charts, Tables, Menus, 和 Panes. FXML(XML-based markup language ) ,可以定义用户接口。 样例程序,快速展示了该技术的特性。 注意: 当前JavaFX应用仅能在Mac系统的桌面上运行。 系统要求 Mac OS下的JavaFX 2.0 开发者预览版需要以下软件: 操作系统 - Mac OS X 10.6 或更高 JDK - JDK 6 u 26 或更高 安装 Mac系统下的javafx文件是zip文件. 安装方法: 从http://www.oracle.com/technetwork/java/javafx/downloads/javafx2-macosx-487281.html下载JavaFX SDK zip文件。 从zip文件解压SDK文件到本地目录。 样例 JavaFX样例程序在http://www.oracle.com/technetwork/java/javafx/downloads/javafx2-macosx-487281.html. 下载该zip文件并解压到包含SDK的目录中,目录结构类似这样: javafx-sdk2.0\     bin\     docs\    javafx-samples-2.0\     rt\     .     . 要在桌面运行应用,双击.jar文件。 每 个样例的源代码都在javafx-samples-2.0\src目录下。想要查看源代码,到javafx-samples-2.0\src \sample目录下,里面有你感兴趣的应用。每个样例的源代码都是NB工程。按照 Setting Up NetBeans IDE with JavaFX 2.0在NB中创建应用。 已知BUG和事项 以下问题被发现存在。后面的数字参考JavaFX issuesdashboard中的报告。 在Dock和菜单栏中应用名称只能是"Java",无法更改。"关于"和"退出"中的名称是"Glass",也不能改。[RT-12113, RT-13010] 当窗口第一次显示时,出现窗口黑屏,原因是不协调的视觉闪光。[RT-16804] 拖拉操作没有用户反馈。[RT-16307, RT-14624] 工作区:收到设置一个游标,并使用一个浮动窗口显示图像来反映拖动操作。 退出时可能异常[RT-14118, RT-15599] 工作区添加应用时悬挂或破碎不太可能。 已知渲染时有掺混。 [RT-12852] 不支持3D渲染。 [RT-13234, RT-13230] Mac OS渐变支持不好. [RT-10565] 不能向用户展示错误对话框. [RT-13739] Caps Lock 在 TextField 和TextArea中无效. [RT-16616] 在TextField中不能剪切、复制、粘贴. [RT-16475] 第四回 在NB中创建JavaFX2.0应用 NetBeans IDE需要一个 JavaFX 2.0可用的Java平台。本部分详述了如何在NB上建立JavaFX2.0可用Java平台。 当你首次新建应用或者打开预加载向导时NetBeans IDE会尝试创建 JavaFX可用的Java平台。如果NB不能自动创建会有警告出现,这时候你必须手动创建. 你可以创建额外的Java平台,比如你想要使用不同的jdk。 这里分两步介绍自动和手动创建Java平台: 打开新建JavaFX应用向导.这是全局的起点,当打开向导时, NetBeans IDE 尝试创建一个JavaFX可用的Java平台。如果 NetBeans 成功创建,你就完成任务了. 手动创建JavaFX可用的平台.如果自动创建失败 , 或者想要添加新的JavaFX可用平台, 你必须手动创建。 打开新建JavaFX应用向导 创 建 JavaFX可用的Java平台的第一步是打开NetBeans IDE的新建JavaFX应用向导。 (或者你可以打开新建JavaFX Preloader 向导。)如果NB没有找到JavaFX可用的Java平台, 会尝试创建。如果成功了,你的新建就完成了。否则你要手动创建。 注意:Windows平台自动创建到默认目录 (C:\Program Files\Oracle\). 打开新建JavaFX Application向导: 选择 JavaFX 分类. 在 Projects窗口, 选择JavaFX Application, 点击Next,打开Name and Location面板 。IDE自动寻找JavaFX SDK并尝试生成 JavaFX可用的Java平台。 NB创建环境的成功与否会出现不同的屏幕: 生 成成功:这个 JavaFX平台使用和IDE相同的 JDK,平台名称叫 Default JFX Platform。 IDE自动选择了这个平台。你成功了,除非你想用不同的jdk创建另外一个可用的平台。若你想这样,按照Creating a JavaFX-Enabled Platform Manually. 生成失败:JavaFX Platform列表没有显示任何可用平台。查看  Creating a JavaFX-Enabled Platform Manually. 手动创建JavaFX可用的平台 打开Java平台管理器NetBeans IDE Java Platform Manager: 在新建 JavaFX Application或者新建JavaFX Preloader向导中,点击Manage Platforms...;或者 点开Tools 菜单选择Java Platforms。或者 打开任意Java工程的Project Properties,切换到Libraries标签,点击Manage Platforms... 点击Add Platform...  Add Java Platform向导中出现Choose Java Platform 面板。找到你想要的jdk. 选中JDK。必须选择JDK 1.6 u 26或更高 (或JDK 7)。点 NexT,打开Platform Name面板。 为你的平台命名后点Finish,返回到 Platform Manager。你创建的平台已经被加入列表了 选中你的平台,打开JavaFX标签。 现在,JavaFX 对该平台还不可用。选中Enable JavaFX框。警告出现了:“the JavaFX platform is invalid.” 点击JavaFX SDK 框旁边的Browse,找到 JavaFX 2.0 SDK文件夹。 点 击Open返回到Java Platform Manager。现在JavaFX SDK 和JavaFX Javadoc 框都填好了。如果JavaFX Runtime 2.0和JavaFX 2.0 SDK在同一目录,JavaFX Runtime 框也会填好的。若JavaFX Runtime 在其他目录,就收到找到Sources狂不用选。 点击Close。你已经完成了。 第五回 开始使用JavaFX2.0 如果想用 JavaFX快速开发富用户体验的应用,就好好读本文吧。我们将创建一个简单应用并了解到用少量代码实现复杂图形效果是多么容易。当然了,JavaFX不仅仅只是异常漂亮生动的形状而已。读完本文后去看看样例将大有好处。 Figure 1 Colorful Circles Application 如果你很熟悉JavaFX场景图形编程模型,那就很容易理解程序的代码。 舞台( stage)是应用的最高层容器,场景( scene)是应用中内容的绘图表面。内容都被组织为场景图形,是一颗节点的层次树。 Figure 2 为应用 ColorfulCircles展示了场景图形。 分支节点是 Group 类的实例化。非分支节点,即叶子节点,是 Rectangle 和Circle 类的实例化。 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() 添加图形 接下来在 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,然后使用一个for循环向其中添加30个圆。每个圆半径到是150,并用white颜色填充,此外不透明度是5%,所以基本是透明的。 然后为这些圆创建边框,代码中包含了 StrokeType 类。 描边类型的 OUTSIDE 标明圆的边界向外扩展 StrokeWidth 的值,也就是4。描边的颜色是 white ,不透明度是16%,使得它比圆的颜色浅。 最后一行把 circles 加到根节点上。这只是临时结构,稍候将修改场景图形使得它匹配 Figure 2 展示那样。 Figure 4 展示了当前应用。由于代码没有为每个圆指定特定位置,它们都叠加在一起,并且窗口的左上角是圆心。层叠的圆的不透明度和黑色背景作用使得圆看起来是灰色的。 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 创建背景渐变 现在创建一个矩形并用线性渐变填充,代码见 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 Figure 7 展示了中间的场景图。现在circles 组和colors 矩形都是根节点的孩子。 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 添加动画 最后一步是使用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 第六回 JavaFX2.0 UI 控件 JavaFX控件是通过API在场景图形中使用结点构建的,所以它们可以使用javaFX平台的富视觉特点。由于javaFX API是完全用Java语言实现的,所以可以轻松将javaFX UI 控件集成进已存在的Java应用中。 JavaFX 2.0中支持的UI控件 UI控件的构造类位于API的javafx.scene.control 包中。 控件列表包括了你可能很熟悉的用Java开发客户端应用的典型UI组件。不过,JavaFX 2.0 SDK引入了新的Java UI 控件,比如TitledPane 和TableView . Figure 1-1 是一副屏幕截图,有三个TitledPane 元素和一个社交类型列表,并且列表可以滑入 (retract)和滑出 (extend). Figure 1-1 Titled Panes 可以从 API文档查看全部UI控件。 UI 控件类比Control类提供了更多的变量和方法,这样就能以直观的方式支持典型的用户交互。你可以使用层叠样式表(CSS)为你的UI组件设计特殊样 式。对于某些个别任务,还可能要继承Control 类来创建定制的UI 组件,或者使用Skin 接口为已存在的控件定义一个新皮肤。 从样例中的Ensemble 应用试着了解下控件的范围、它们的行为、可以实现的样式。 特性和效果 由于javafx.scene.control包中的 UI 控件都继承了 Node 类,所以它们可以和场景图形的渲染、动画、变换及动画过度进行整合。 考虑创建一个按钮,为它添加倒影并通过时间线修改它的透明度来让它闪动。 Figure 1-2 展示了这个按钮的三个不同时间线上的状态。左边的图像是不透明度设为1.0时 ,中间的图像是不透明度 0.8 ,最右边的不透明度是0.5 . Figure 1-2 Animated Button 通过使用JavaFX API只用少量代码就能实现这个任务。 Example 1-1 创建了一个无限的时间线并开始了它,里面有一个600毫秒的关键帧设置按钮的不透明度从默认值(1.0)向 0.0变化。setAutoReverse 使得时间线可以自动反向。 Example 1-1 Creating an Animated Button 复制代码 1. import javafx.animation.KeyFrame; 2. import javafx.animation.KeyValue; 3. import javafx.animation.Timeline; 4. import javafx.util.Duration; 5. import javafx.scene.control.Button; 6. import javafx.scene.text.Font; 7. import javafx.scene.effect.Reflection; 8. 9. ... 10. Button button = new Button(); 11.     button.setText("OK"); 12.     button.setFont(new Font("Tahoma", 24)); 13.     button.setEffect(new Reflection()); 14. 15. final Timeline timeline = new Timeline(); 16. timeline.setCycleCount(Timeline.INDEFINITE); 17. timeline.setAutoReverse(true); 18. final KeyValue kv = new KeyValue(button.opacityProperty(), 0); 19. final KeyFrame kf = new KeyFrame(Duration.millis(600), kv); 20. timeline.getKeyFrames().add(kf); 21. timeline.play(); ... 你也可以应用 javafx.scene.effect 包中的其他效果,比如shadow, lighting, 或者 motion blur. 为UI控件添加CSS装饰 通过定义自己的Cascading Style Sheets (CSS)可以定制内建的UI控件。在JavaFX 应用中使用CSS很像在HTML中使用,因为都必须遵循相同的CSS 规范。控件的视觉效果由.css文件定义,见代码Example 1-2 . Example 1-2 Defining Styles for UI Controls in the CSS File /*controlStyle.css */ .scene{     -fx-font: 14pt "Cambria Bold";     -fx-color: #e79423;     -fx-background: #67644e; } .button{     -fx-text-fill: #006464;     -fx-background-color: #e79423;     -fx-border-radius: 20;     -fx-background-radius: 20;     -fx-padding: 5; } 可以通过Scene类中的 getStylesheets 方法应用该效果,见Example 1-3 . Example 1-3 Applying CSS Scene scene = new Scene(); scene.getStylesheets().add("uicontrolssample/controlStyle.css"); //译者注:添加外部css文件时,即使文件和类在同意目录下,也要加上css所在的包名。 另外,你可以通过使用setStyle方法直接定义控件风格。 Example 1-4 中的 -fx-base 属性为场景中新增的双态按钮定义,它重写了CSS文件中对应的属性。 Example 1-4 Defining the Style of a Toggle Button in the JavaFX Application ToggleButton tb3 = new ToggleButton ("I don't know"); tb3.setStyle("-fx-base: #ed1c24;"); Figure 1-3 展示了双态按钮的效果。 Figure 1-3 Applying CSS Style to a Toggle Button 图表 除 了为用户接口提供典型元素外, JavaFX SDK在 javafx.scene.chart包中 提供了预置图表。以下类型图表已经可以支持了:area chart, bar chart, bubble chart, line chart, pie chart, and scatter chart。一个图表可以包含几个系类的数据。 Figure 1-4 是一个进口水果饼图。 Figure 1-4 Pie Chart 和其他Java客户端工具不同,使用JavaFX SDK 只需要在应用中添加几行代码就能构建这样的图表。你也可以定义一系列的颜色表和风格、应用视觉效果、处理鼠标事件、创建动画等。 来 Using JavaFX Charts 了解更多的图表特性和功能信息。 集成JavaFX 2.0 UI 控件和 Swing 可以将 JavaFX UI 控件集成进已存在的用Swing构建的Java客户端应用。 要集成JavaFX内容和Swing,请安装以下步骤: 1.将JavaFX UI 控件一个一个地添加到javafx.scene.Scene 对象中的布局容器中,比如一个group. 2.把Scene 对象加入Swing 应用. 即使把一个单独的JavaFX 2.0 控件加入到已存在的Swing代码中也要做上面的两个步骤。 尽管它们被集成进了Swing程序,JavaFX 2.0 UI 控件也依然被菱镜图形库(Prism graphical library)渲染 ,并具有全部的高级渲染能力。 到第七回 了解更多二者的集成信息。 第七回 JavaFX2.0和Swing的集成 JavaFX 2.0 发布版引入了JFXPanel类,它位于 javafx.embed.swing 包中,使你能够将JavaFX内容绑定进Swing 程序。 本文教你怎么将JavaFX内容加进Swing 程序中并指导你如何在一个同时具备Swing 和JavaFX 的应用中正确使用线程。 添加JavaFX到Swing组件 要达到本文的目的,你要创建一个JFrame 组件,并添加一个JFXPanel 对象,JFXPanel组件的图形场景要包含JavaFX 内容。 和 所有的Swing程序一样,你要在 Event Dispatch Thread (事件调度线程EDT)上创建图形用户接口(GUI) 。Example 1 展示了initAndShowGUI 方法,它创建了一个JFrame 组件并添加了JFXPanel 对象。创建JFXPanel 类的实例会在后台开始JavaFX 运行时。GUI 创建后,调用initFX 方法在JavaFX线程上创建JavaFX场景。 Example 1 复制代码 1. public class Test { 2. 3.     private static void initAndShowGUI() { 4.         // This method is invoked on the EDT thread 5.         JFrame frame = new JFrame("FX"); 6.         final JFXPanel fxPanel = new JFXPanel(); 7.         frame.add(fxPanel); 8.         frame.setVisible(true); 9. 10.         Platform.runLater(new Runnable() { 11.             @Override 12.             public void run() { 13.             initFX(fxPanel); 14.             } 15.        }); 16.     } 17. 18.     private static void initFX(JFXPanel fxPanel) { 19.         // This method is invoked on the JavaFX thread 20.         Scene scene = createScene(); 21.         fxPanel.setScene(scene); 22.     } 23. 24.     private static Scene createScene() { 25.         //Code to create the JavaFX scene 26.     } 27.      28.     public static void main(String[] args) { 29.         SwingUtilities.invokeLater(new Runnable() { 30.             @Override 31.             public void run() { 32.             initAndShowGUI(); 33.             } 34.         }); 35.     } 36. } JavaFX-Swing的互操作性和线程 由于JavaFX和Swing的数据存在与一个程序中,你可能遇到以下互操作的情况: 一个JavaFX的数据改变是由Swing的数据改变引起的. 反之. 改变JavaFX的数据来回应Swing数据的改变 记住JavaFX的数据只能通过javaFX用户线程访问。不论何时要改变JavaFX 数据都要把你的代码用一个Runnable 对象包围起来并调用Platform.runLater 方法。见Example  2 . Example 2 复制代码 1. jbutton.addActionListener(new ActionListener() { 2.     public void actionPerformed(ActionEvent e) { 3.         Platform.runLater(new Runnable() { 4.             @Override 5.             public void run() { 6.                 fxlabel.setText("Swing button clicked!"); 7.             } 8.         }); 9.     } 10. }); 改变Swing数据来回应JavaFX 数据的改变 记住Swing数据的改变要通过EDT。确保你的代码实现了EDT,把它用Runnable 对象环绕并调用SwingUtilities.invokeLater 方法。见Example  3 . Example 3 复制代码 1. SwingUtilities.invokeLater(new Runnable() { 2.     @Override 3.     public void run() { 4.         //Code to change Swing data. 5.     } 6. }); SwingBrowser2: 一个集成了JavaFX组件的Swing应用 通 过SwingBrowser2 可以看到 Swing - JavaFX是怎么互操作的,它为浏览器提供了常用的基本功能。你可以在它的地址栏中键入一个URL 来查看应用窗口加载的页面。你可以通过点页面链接去到新页面、返回前一页、打开标签页、加入书签、全页面检索。 Figure 1是该应用的窗口。 Figure 1 The SwingBrowser2 application window 初始化Swing数据 你可以从一个NB工程中的侧边栏点链接来下载swingbrowser2.zip 文件。解压到本地并从Netbeans IDE中运行为工程。确保你的NetBeans IDE版本是支持的。 SwingBrowser2打开后,它的GUI创建就在 EDT上。通过边栏的链接查看 Main.java 文件。 应用的顶层窗口是一个 JFrame 组件,包含了很多Swing 组件,比如一个tabbed面板,一个menu,几个text field、button,还有一个要显示JavaFX内容的JFX 面板。 加载 JavaFX内容 刚开始运行,JFX面板包含一个空的WebView 对象。当在地址栏输入一个URL后, AddressBar.java中的 action listener 就开始加载页面。代码见Example 4 . Example 4 复制代码 1. txtURL.addActionListener(new ActionListener() { 2.     @Override public void actionPerformed(ActionEvent e) { 3.         browser.load(txtURL.getText()); 4.     } 5. }); Browser.java文件中的load 方法验证URL并调用call方法,见 Example 5 . Example 5 复制代码 1. public void load(String str) { 2.     if (str != null { 3.         str = str.trim(); 4. 5.         if (str.isEmpty()) return; 6. 7.         String url = toURL(str); 8.         if (url == null) { 9.             url = toURL("http://" + str); 10.         } 11.         if (url != null) { 12.         Platform.runLater (new Runnable() { 13.             @Override 14.             public void run () { 15.      16.         call(url); 17.         } 18.     } 19. } 20. 21. private static String toURL(String str) { 22.     try { 23.         return new URL(str).toExternalForm(); 24.     } catch (MalformedURLException exception) { 25.         return null; 26.     } 27. } JavaFX数据应该只能在JavaFX线程上访问。call方法验证了线程并确保特点URL的页面被加载在 JavaFX线程上。Callback.java 文件中有完整代码。Example 6 是表明了实现方案的代码块。 Example 6 复制代码 1. public final void call(final String value) { 2.     if (Platform.isFXApplicationThread()) { 3.         callImpl(value); 4.     } 5.     else { 6.         Platform.runLater(new Runnable() { 7.             @Override public void run() { 8.                 callImpl(value); 9.             } 10.         }); 11.     } 12. } 13. 14. protected void callImpl(String value) { 15.     getEngine().load(value); 16. } 更新Swing数据 当 WebView 组件加载了新页面后,页面标题就从JavaFX数据中取回并传递给Swing GUI。然后页面标题就显示在标签页上并加入应用标题。 TabbedBrowser.java 中有完整代码。Example 7 是表明了实现方案的代码块。 Example 7 复制代码 1. public WebPane addNewTab(final String url, boolean selected) { 2.     ... 3.     final WebPane wp = new WebPane(url); 4. 5.     wp.getBrowser().getEngine().titleProperty().addListener( 6. new javafx.beans.value.ChangeListener() { 7.         @Override 8.         public void changed(ObservableValue observable, 9. String oldValue, final String title) { 10.             EventQueue.invokeLater(new Runnable() { 11.                 @Override public void run() { 12.                     setTitleAt(indexOfComponent(wp), title); 13.                     setToolTipTextAt(indexOfComponent(wp), title); 14. 15.                     if (getSelectedComponent() == wp) { 16.                     setWindowTitle(title); 17.                     } 18.                 } 19.             )}; 20.         } 21.     )}; 22. } 想了解怎么部署二者的整合应用,到 Deploying JavaFX Applications  查看。 第八回 JavaFX2.0 标签Label Label 类位于 JavaFX API的 javafx.scene.control 包中,它继承了 Labeled 类。Label 类用来显示一个文本元素。你可以让一个文本换行来适应特定大小的空间,也可以加入图像。 Figure 2-1 展示了三个标签的常规用法。左边是带有图像的文本元素,中间的是转动后的文本,右边的是换行文本。 Figure 2-1 Sample Application with Labels 创建Label JavaFX API提供了三个Label 类的构造方法来创建标签,见代码Example 2-1 . Example 2-1 Creating Labels //空标签 Label label1 = new Label(); //有文本的标签 Label label2 = new Label("Search"); //有文本有图像 Image image = new Image(getClass().getResourceAsStream("labels.jpg")); Label label3 = new Label("Search", new ImageView(image)); 创建标签后就可以用下面Labeled 类的方法向其中添加文本和图像内容。 setText(String text) – 为标签指定一个标题。 setGraphic(Node graphic) – 指定图标 setTextFill方法为标签的文本元素指定了颜色。研究下 Example 2-2:先创建了一个文本标签,又添加了一个图标,再指定文本的填充颜色。 Example 2-2 Adding an Icon and Text Fill to a Label Label label1 = new Label("Search"); Image image = new Image(getClass().getResourceAsStream("labels.jpg")); label1.setGraphic(new ImageView(image)); label1.setTextFill(Color.web("#0076a3")); 这块代码加入到程序中后,就产生了一个标签,见Figure 2-2 . Figure 2-2 Label with Icon 当为按钮定义文本和图像内容时,可以用setGraphicTextGap 方法在之间产生空白。 另 外,可以在标签的布局设置区域使用setTextAlignment 方法来改变标签的位置。你也可以通过 setContentDisplay 方法为图像定义针对文本的相对位置,指定下面任意一个ContentDisplay常量: LFFT , RIGHT , CENTER , TOP , BOTTOM . 设置字体 比较一下 Figure 2-1 和 Figure 2-2 中的搜索标签,注意Figure 2-1 中的标签是大字体。这是因为Example 2-2 中的代码块没有为标签指定任何字体,它被默认文字大小渲染的。 使用Labeled 类的 setFont 方法为标签提供不同于默认值的文字大小。Example 2-3 在的代码块将label1 的文字大小设置为30号并且字体名称是Arial。为label2 设置的是32号和Cambria字体。 Example 2-3 Applying Font Settings //Use a constructor of the Font class label1.setFont(new Font("Arial", 30)); //Use the font method of the Font class label2.setFont(Font.font("Cambria", 32)); 换行文本 创建标签后,有时候必须让文本适应这个比它小的标签。 这时必须要打断文本 (换行) 来使它适应布局区域,为 setWrapText 方法设置true值即可。见 Example 2-4。 Example 2-4 Enable Text Wrapping Label label3 = new Label("A label that needs to be wrapped"); label3.setWrapText(true); 把label3加入程序后,效果如Figure 2-3 . Figure 2-3 Label with Wrapped Text 但 是如果标签的布局区域不仅仅是被限制了宽,还限制了高呢?当标签不可能渲染全部文本串时你可以为它指定行为。使用Labeled 类的setTextOverrun 方法和任一OverrunStyle 类型定义如何合适的处理只能部分显示的文本。查询API 文档来了解更多关于OverrunStyle 类型的信息。 使用特效 尽管标签是静态内容不能修改,不过依然可以应用特效或者变换它。 Example 2-5 中的代码块将label2 转动了270 °并且垂直方向平移了50。 Example 2-5 Rotating a Label Label label2 = new Label ("Values"); label2.setFont(new Font("Cambria", 32)); label2.setRotate(270); label2.setTranslateY(50); 旋 转和平移是 JavaFX API中的典型变换。此外,你可以为标签设置当用户让鼠标悬停时具有变焦效果 (放大)。Example 2-6 中的代码块为label3应用了变焦效果。 当标签的MOUSE_ENTERED 事件被激发时,setScaleX 和setScaleY 方法设置其缩放比例是1.5。当用户的鼠标离开标签时MOUSE_EXITED 时间发生,缩放比例设为1.0标签就变成原始大小了。 Example 2-6 Applying the Zoom Effect 复制代码 1. label3.setOnMouseEntered(new EventHandler() { 2.     @Override public void handle(MouseEvent e) { 3.         label3.setScaleX(1.5); 4.         label3.setScaleY(1.5); 5.     } 6. }); 7. 8. label3.setOnMouseExited(new EventHandler() { 9.     @Override public void handle(MouseEvent e) { 10.         label3.setScaleX(1); 11.         label3.setScaleY(1); 12.     } 13. }); Figure 2-4   展示了label3的两个状态。 Figure 2-4 Zooming a Label 第九回 JavaFX2.0按钮Button Button类可通过JavaFX API让用户通过点击一个按钮来处理某个行为。Button类是Labeled 类的子类,它可以显示文本、图片(当然也可以同时显示二者)。Figure 3-1展示了具有各种效果的按钮。通过本文你就能学会怎么创建这样的按钮。 Figure 3-1 Types of Buttons 创建按钮 在JavaFX应用中有三种Button类的构造方法创建 Button 控件,见Example 3-1 . Example 3-1 Creating a Button 复制代码 1. //A button with an empty text caption. 2. Button button1 = new Button(); 3. //A button with the specified text caption. 4. Button button2 = new Button("Accept"); 5. //A button with the specified text caption and icon. 6. Image imageOk = new Image(getClass().getResourceAsStream("ok.png")); 7. Button button3 = new Button("Accept", new ImageView(imageOk)); 由于 Button 类继承了Labeled 类,所以你可以用下面的方法为没有图标和标题的按钮指定内容。 setText(String text) 方法–为按钮指定标题 The setGraphic(Node graphic) 方法– 指定图标 Example 3-2展示了如何创建无标题的图标按钮。 Example 3-2 Adding an Icon to a Button Image imageDecline = new Image(getClass().getResourceAsStream("not.png")); Button button5 = new Button(); button5.setGraphic(new ImageView(imageDecline)); 将代码块加入应用后产生的效果如 Figure 3-2 . Figure 3-2 Button with Icon Example 3-2 和Figure 3-2 中的图标是一个ImageView 对象。不过,也可以使用其他的图形对象,如javafx.scene.shape 包中的各种形状。当按钮同时具有文本和图形内容时,可以使用setGraphicTextGap方法在二者之间设置间隙。 Button类的默认皮肤有如下的视觉区别。Figure 3-3 用一个图标按钮展示了这种区别。 Figure 3-3 Button States 分配行为 按钮的最基本功能就是在按下时处理行为。用Button 类的setOnAction方法定义用户按下按钮时会发生什么。 Example 3-3 是为button2定义行为的代码块。 Example 3-3 Defining an Action for a Button button2.setOnAction(new EventHandler() {     @Override public void handle(ActionEvent e) {         label.setText("Accepted");     } }); ActionEvent 是由EventHandler处理的一种事件类型。EventHandler对象提供了handle方法处理按钮触发的行为。Example 3-3展示了如何重写handle 方法,所以当用户按下button2 时label 的标题就变为了"Accepted." 可以用Button 类设置任意多你需要引起特定行为或应用视效的事件处理方法。 应用特效 由于 Button类继承了Node 类,你可以使用javafx.scene.effect包中的任意特效改善按钮的视觉呈现。在Example 3-4中,当onMouseEntered 事件发生时为button3应用了DropShadow效果。 Example 3-4 Applying the DropShadow Effect 复制代码 1. DropShadow shadow = new DropShadow(); 2. //Adding the shadow when the mouse cursor is on 3. button3.addEventHandler(MouseEvent.MOUSE_ENTERED, 4.     new EventHandler() { 5.         @Override public void handle(MouseEvent e) { 6.             button3.setEffect(shadow); 7.         } 8. }); 9. //Removing the shadow when the mouse cursor is off 10. button3.addEventHandler(MouseEvent.MOUSE_EXITED, 11.     new EventHandler() { 12.         @Override public void handle(MouseEvent e) { 13.             button3.setEffect(null); 14.         } 15. }); Figure 3-4是鼠标在button3 上移入移出的效果。 Figure 3-4 Button with Drop Shadow 为Button造型 下面对按钮的美化工作是通过Skin 类定义的CSS风格来实现的。在JavaFX 2.0 中使用CSS和在HTML中使用很类似,因为它们都基于相同的CSS规范。 可 以先定义一个单独的CSS文件,然后用 setStyleClass 方法在应用中使用它。这个方法也是继承自Node类的且对全部UI控件可用。当然你也可以使用setStyle方法直接在代码中定义按钮的风格。 Example 3-5 和Figure 3-4演示了后面这种方法。 Example 3-5 Styling a Button button1.setStyle("-fx-font: 22 arial; -fx-base: #b6e7c9;"); -fx-font属性为button1设置了字体大小,-fx-base属性改变了按钮默认的颜色。这样,button1就具有了浅绿色和大字体,如Figure 3-5 . Figure 3-5 Button Styled with CSS 第十回 JavaFX2.0单选框Radio Button RadioButton类是ToggleButton类的一个专业实现。一个单选按钮控件可以被选中和取消选中。典型的单选按钮是被放置在一个组里面,组里每次只能有一个按钮被选中。这种行为将它们和开关按钮区别开了,因为一个组中的所有开关按钮能同时被取消选中。 Figure 4-1是三幅RadioButton例子的截图,里面的三个单选按钮在同一个组中。 Figure 4-1 RadioButton Sample 通过研习下文能够了解更多关于在应用中实现单选按钮的信息。 创建RadioButton RadioButton 类位于JavaFX SDK的javafx.scene.control包中,提供了两个创建单选按钮的构造方法。Example 4-1是创建两个单选按钮。无参数构造方法用来创建rb1,它的标题通过setText方法设置。而rb2的标题直接定义在相应的构造方法中。 Example 4-1 Creating Radio Buttons 复制代码 1. //A radio button with an empty string for its label 2. RadioButton rb1 = new RadioButton(); 3. //Setting a text label 4. rb1.setText("Home"); 5. //A radio button with the specified label 6. RadioButton rb2 = new RadioButton("Calendar"); 你可以通过为setSelected方法指定true值来明确地让一个单选按钮是选中状态。如果你想要检查一个特定的单选按钮是否被用户选中了,使用isSelected方法。 由于 RadioButton 类继承了Labeled 类,所以你不仅可以为其指定文本标题,还可以是图片。使用setGraphic方法来指定一副图片。Example 4-2演示了如何在应用中实现带图像的单选按钮。 Example 4-2 Creating a Graphical Radio Button 复制代码 1. Image image = new Image(getClass().getResourceAsStream("ok.jpg")); 2. RadioButton rb = new RadioButton("Agree"); 3. rb.setGraphic(new ImageView(image)); 将Radio Button加入到组 单 选按钮的典型用法是在组中使用来提供几个互斥选项。ToggleGroup对象为所有的单选按钮提供了引用来关联自身,并且管理单选按钮来实现每次只能有 一个被选中。Example 4-3创建了一个开关按钮组、三个单选按钮,把每个按钮都加入到组中,并指定了在程序启动后哪个要被选中。 Example 4-3 Creating a Group of Radio Buttons 复制代码 1. final ToggleGroup group = new ToggleGroup(); 2. RadioButton rb1 = new RadioButton("Home"); 3. rb1.setToggleGroup(group); 4. rb1.setSelected(true); 5. RadioButton rb2 = new RadioButton("Calendar"); rb2.setToggleGroup(group); 6. RadioButton rb3 = new RadioButton("Contacts"); 7. rb3.setToggleGroup(group); 当这些单选按钮被它们的布局管理器添加到应用的内容上以后,输出应该类似于Figure 4-2. Figure 4-2 Three Radio Buttons Combined in a Group 处理Radio Button事件 当组中的某个单选按钮被选中时程序会处理该行为。研读Example 4-4中的代码块来了解怎么根据哪个单选按钮被选中来改变图标。 Example 4-4 Processing Action for Radio Buttons 复制代码 1. ImageView image = new ImageView(); 2. rb1.setUserData("Home"); 3. rb2.setUserData("Calendar"); 4. rb3.setUserData("Contacts"); 5. final ToggleGroup group = new ToggleGroup(); 6. group.selectedToggleProperty().addListener( 7. new ChangeListener() 8. { public void changed(ObservableValue ov, 9. Toggle old_toggle, Toggle new_toggle) 10. { if (group.getSelectedToggle() != null) 11. { final Image image = 12. new Image( getClass().getResourceAsStream( group.getSelectedToggle().getUserData().toString() + ".jpg" ) ); 13. icon.setImage(image); } } }); 比如,当rb3被选中时,getSelectedToggle方法返回"rb3,"getUserData方法返回"Contacts"。因此,getResourceAsStream方法接收了"Contacts.jpg."Figure 4-1是应用的输出。 为Radio Button请求焦点 在单选按钮组中,默认第一个按钮具有焦点。当你为组中的第二个单选按钮使用setSelected 方法后,你期望的结果是像Figure 4-3. Figure 4-3 Default Focus Settings 第二个按钮被选中了,但焦点依然在第一个按钮上。使用requestFocus函数可以改变焦点位置,见Example 4-5. Example 4-5 Requesting Focus for the Second Radio Button rb2.setSelected(true); rb2.requestFocus(); 这样,代码产生的结果如Figure 4-4. Figure 4-4 Setting Focus for the Selected Radio Button 第十一回 JavaFX2.0开关按钮ToggleButton ToggleButton类代表了可以通过JavaFX API创建的另一类按钮。 2个或更多的这种按钮被加入一个组中,但是每次只能有一个被选中,或者一个都没有。 Figure 5-1是在一个组中有3个开关按钮的应用截图。这个应用根据哪个开关按钮别按下来决定绘制何种颜色的矩形。 Figure 5-1 Three Toggle Buttons 创建Toggle Button 可以通过ToggleButton类的3个构造方法的任意一个来创建开关按钮。见Example 5-1. 复制代码 1. //A toggle button without any caption or icon 2. ToggleButton tb1 = new ToggleButton(); 3. //A toggle button with a text caption 4. ToggleButton tb2 = new ToggleButton("Press me"); 5. //A toggle button with a text caption and an icon 6. Image image = new Image(getClass().getResourceAsStream("icon.png")); 7. ToggleButton tb3 = new ToggleButton ("Press me", new ImageView(image)); ToggleButton类继承了Labeled类,因而你可以为其指定文本标题、图像、文本加图像。可以使用Labeled  类的setText和setGraphic方法来给开关按钮指定文本和图像。 在代码中定义了开关按钮后,就可以把它们放进组中并指定特定的行为。 把Toggle Button加入组 ToggleButton类的实现和RadioButton类实现相当类似。不过和单选框不同的是,开关按钮并不要求每次必须最少有一个按钮被选中在组中。也就是说,点击选中的开关按钮会使其取消选中,但是点击组中的单选按钮没任何反应。 t花点时间看下 Example 5-2中的代码吧. 复制代码 1. Example 5-2 Combining Toggle Buttons in a Group 2. final ToggleGroup group = new ToggleGroup(); 3. 4. ToggleButton tb1 = new ToggleButton("Minor"); 5. tb1.setToggleGroup(group); 6. tb1.setSelected(true); 7. 8. ToggleButton tb2 = new ToggleButton("Major"); 9. tb2.setToggleGroup(group); 10. 11. ToggleButton tb3 = new ToggleButton("Critical"); 12. tb3.setToggleGroup(group); Example 5-2创建了3个开关按钮并把它们加入开关组中。tb1调用了setSelected方法,所以应用打开后它会被按下。但是,你也可以按起Minor按钮使得没有任何开关按钮被按下。见 Figure 5-2. Figure 5-2 Three Toggle Buttons in a Group Description of "Figure 5-2 Three Toggle Buttons in a Group" 一般使用开关按钮的组来为每个按钮分配特定行为。下一部分将解释如何使用这些开关按钮改变矩形的颜色。 设置行为 ToggleButton类从Node 类继承了setUserData方法,该方法可以让你给任意选中的选项赋特定的值。在 Example 5-3中,用户数据指明了要用什么颜色来绘制矩形。 复制代码 1. Example 5-3 Setting User Data for the Toggle Buttons 2. tb1.setUserData(Color.LIGHTGREEN); 3. tb2.setUserData(Color.LIGHTBLUE); 4. tb3.setUserData(Color.SALMON); 5. 6. final Rectangle rect = new Rectangle(145, 50); 7. 8. final ToggleGroup group = new ToggleGroup(); 9. group.selectedToggleProperty().addListener(new ChangeListener(){ 10.     public void changed(ObservableValue ov, 11.         Toggle toggle, Toggle new_toggle) { 12.             if (new_toggle == null) 13.                 rect.setFill(Color.WHITE); 14.             else 15.                 rect.setFill( 16.                     (Color) group.getSelectedToggle().getUserData() 17.                 ); 18.          } 19. }); ChangeListener对象检查了组中被按下的开关。如果没有开关按钮被按下,矩形就用白色绘制。如果某个按钮被按下,getSelectedToggle和getUserData方法会连续调用来返回一种颜色绘制矩形。 比如用户按下了tb2按钮, setSelectedToggle().getUserData()的调用返回 Color.LIGHTBLUE结果如图 Figure 5-3. Figure 5-3 Using Toggle Buttons to Paint a Rectangle Description of "Figure 5-3 Using Toggle Buttons to Paint a Rectangle" 查看 ToggleButtonSample.java文件检查应用的完整代码。 美化Toggle Button 可以通过为开关按钮应用CSS来改善应用的视效。在JavaFX 2.0应用中使用CSS和在HTML中使用几乎一样,因为它们都基于相同的CSS规范。Example 5-4使用setStyle方法改变了开关按钮的 -fx-base CSS熟悉。 Example 5-4 Applying CSS Styles to Toggle Buttons tb1.setStyle("-fx-base: lightgreen;"); tb2.setStyle("-fx-base: lightblue;"); tb3.setStyle("-fx-base: salmon;"); 程序中加入这些代码后开关按钮的变化见 Figure 5-4. Figure 5-4 Painted Toggle Buttons Description of "Figure 5-4 Painted Toggle Buttons" 你可以尝试ToggleButton类的其他CSS属性,或者应用动画、转换、 JavaFX API中的各种视效。 第十二回 JavaFX2.0 复选框CheckBox CheckBox类让你可以在应用中创建复选框。尽管复选框看起来很想单选框 ,但是它们并不能被放置进一个开关组中来实现很多选项的选择。你可以复习一下前面关于Radio Button 和Toggle Button相关的内容。 Figure 6-1  是有三个复选框的一个应用,这些复选框用来控制工具栏中的图标是否显示。 Figure 6-1 Checkbox Sample 创建Checkbox Example 6-1   creates two simple checkboxes. 复制代码 1. //Example 6-1 Creating Checkboxes 2. 3. //A checkbox without a caption 4. CheckBox cb1 = new CheckBox(); 5. //A checkbox with a string caption 6. CheckBox cb2 = new CheckBox("Second"); 7. cb1.setText("First"); 8. cb1.setSelected(true); 创建复选框后,就可以使用JavaFX API中的方法对它们进行设置。在Example 6-1 中 setText方法为 c1这个复选框定义了标题,设置setSelected方法为 true以使程序运行后 cb1是选中的。 定义状态 复 选框能被定义为明确或不明确。被定义为明确后就可以被选中或取消选中,但是不明确的话就不能被选中或取消选中。结合使用CheckBox类的 setSelected   和setIndeterminate方法来为复选框指定状态。 Table 6-1 是三个不同状态的复选框,它们的 INDETERMINATE   和SELECTED属性不相同。 Table 6-1 States of a Checkbox 当 复选框要呈现多种状态的UI元素时要为它们指定三种状态,如"Yes", "No", "Not Applicable"。CheckBox  类的 allowIndeterminatePropety设置了复选框对象是否能在全部三种状态(选中、取消选中、不明确)中循环 。如果设置了 ,这个控件就可以在三种状态中循环。否则 ,这控件只能在选中和取消选中两种状态中切换。下一部分的应用中构造了三个复选框,并且只有两个状态。 设置行为 Example 6-2 中的代码块创建了三个复选框,这样当一个复选框被选中后,相应的图标就显示出来。 复制代码 1. //Example 6-2 Setting the Behavior for the Checkboxes 2. 3. final String[] names = new String[]{"Security", "Project", "Chart"}; 4. final Image[] images = new Image[names.length]; 5. final ImageView[] icons = new ImageView[names.length]; 6. final CheckBox[] cbs = new CheckBox[names.length]; 7. for (int i = 0; i < names.length; i++) 8. { final Image image = images[i] = 9. new Image(getClass().getResourceAsStream(names[i] + ".png")); 10. final ImageView icon = icons[i] = new ImageView(); 11. final CheckBox cb = cbs[i] = new CheckBox(names[i]); 12. cb.selectedProperty().addListener 13. (new ChangeListener() { 14. public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) 15. { icon.setImage(new_val ? image : null); 16. } }); 17. } names这个数组使用了一个 for循环来创建复选框和相应的图标。比如说, cbs[0]是第一个复选框,被分配了"Security"这个标题;同时image[0]接收了"Security.png"作为 getResourceStream方法的文件名。如果一个特定的复选框被选中后,相应的图片就被分配成它的图标。如果一个复选框被取消选中,图标接收 null图片所以没有图标分配。 Figure 6-2  是应用中的 Security 和Chart 复选框被选中,Project 取消选中。 Figure 6-2 Checkbox Application in Action 美化Checkbox Figure 6-2  中的复选框有CheckBox  类 默认的外观和感觉。可以通过使用 setStyle  方法来改变它的样子,见 Example 6-3 . 复制代码 1. //Example 6-3 Styling a Checkbox 2. 3. cb1.setStyle( "-fx-border-color: lightblue; " 4. + "-fx-font-size: 20;" 5. + "-fx-border-insets: -5; " 6. + "-fx-border-radius: 5;" 7. + "-fx-border-style: dotted;" 8. + "-fx-border-width: 2;" ); 新风格包括了一条浅蓝色点边框和一个扩大字体的标题。Figure 6-3  是使用了这种风格的 cb1复选框。 Figure 6-3 Styled Checkbox Description of "Figure 6-3 Styled Checkbox" 要为应用中的所有复选框设置特定的风格,按照下面的步骤: 创建一个 .css文件; 在 .css文件中创建 checkbox  CSS 类。 在checkbox   CSS 类中定义所有需要的风格。 在你的JavaFX应用中,使用setStyleClass  方法来使用CSS文件。 第十三回 JavaFX2.0 选项框ChoiceBox ChoiceBox类为在几个选项中快速选择提供了支持。看看 Figure 7-1 中的选项框的这个简单实现怎么样。 Figure 7-1 Creating a Choice Box with Three Items Description of "Figure 7-1 Creating a Choice Box with Three Items" 创建 Choice Box Example 7-1 创建了带有三个条目的选项框。 Example 7-1 Creating a Choice Box ChoiceBox cb = new ChoiceBox(FXCollections.observableArrayList(     "First", "Second", "Third") ); Example 7-1 是创建在ChoiceBox类的构造函数中条目列表。这个条目列表是用一个可见的数组指定的。当然你可以选择使用该类的空构造方法,然后使用setItems方法设置条目列表。见Example 7-2. Example 7-2 Choice Box with Text Elements and a Separator ChoiceBox cb = new ChoiceBox(); cb.setItems(FXCollections.observableArrayList(     "New Document", "Open ",     new Separator(), "Save", "Save as") ); 不过选项框不单单能包含文本元素,还可以是其他对象。  Example 7-2中用一个Separator控件分开了条目。当应用中加入这些代码后,效果见 Figure 7-2. Figure 7-2 Menu Created by Using a Choice Box Description of "Figure 7-2 Menu Created by Using a Choice Box" 实际项目中,选项框用来创建多选列表。 为Choice Box设置行为 Figure 7-3 中的应用提供了一个带有5个选项的多选框。当一种特定的语言被选中后,相应的问候语就显示出来。 Figure 7-3 Multiple-Choice List Description of "Figure 7-3 Multiple-Choice List" Figure 7-4 中的代码块解释了选项框中的条目被选中后是怎么定义哪中语言的问候语应该显示的。 Figure 7-4 Selecting a Choice Box Item 通 过连续不断的调用getSelectionModel和selectedIndexProperty方 法,ChangeListener 对象侦测当前被选中条目的索引。getSelectionModel方法返回被选中的条目,selectedIndexProperty方法返回选项框的 SELECTED_INDEX属性。这样,索引的整型值定义了问候数组中的元素,并为标签指定一个 String文本。如果一个用户选择了第二个条目,它对应的西班牙语,SELECTED_INDEX就是1,"Hola"就被从问候语数字中选了出来。这 样,标签就显示"Hola." 可以通过为ChoiceBox控件分派提示条使它更有引导性。提示条是javafx.scene.control包中的一个UI控件。提示条能被用于任何JavaFX UI控件。 使用Tooltip Tooltip类为在选项框(还有其他所有控件)添加提示条提供了预制方法,可以通过调用setTooltip方法很容易的使用。见Example 7-3. Example 7-3 Adding a Tooltip to a Choice Box cb.setTooltip(new Tooltip("Select the language")); 一般用户是在Tooltip类的构造方法内定义提示内容。不过,如果你的应用逻辑要求UI动态地设置文本,你可以使用空构造方法使用它,然后使setText方法为它设置文本。 选项框使用了提示条后,用户把鼠标放在选项框上就看到一个图像条。见 Figure 7-5. Figure 7-5 Choice Box with the Applied Tooltip Description of "Figure 7-5 Choice Box with the Applied Tooltip" 要改善应用的外观,可以给选项框使用CSS,或者使用视觉效果和变换。 第十四回 JavaFX2.0 文本框TextField TextField类实现了一种可以接受和显示文本输入的UI控件,它提供了接受用户输入的功能。和另一个文本输入控件PasswordField一起都继承了TextInput这个类,TextInput是所有文本控件的父类。 Figure 8-1 是一个带有标签的典型文本框。 Figure 8-1 Label and Text Field Description of "Figure 8-1 Label and Text Field" 创建Text Field 在 Example 8-1中,一个文本框和一个标签被用来显示输入的内容类型。 Example 8-1 Creating a Text Field 复制代码 1. Label label1 = new Label("Name:"); 2. TextField textField = new TextField (); 3. HBox hb = new HBox(); 4. hb.getChildren().addAll(label1, textField); 5. hb.setSpacing(10); 你 可以像 Example 8-1中那样创建空文本框或者是创建带有特定文本数据文本框。要创建带有预定义文本的文本框,使用下面这个TextField类的构造方 法:TextField("Hello World!")。任何时候你都可以通过getText 方法获得一个文本框的值。 可以使用TextInput 类的setPrefColumnCount方法设置文本框的大小,定义文本框一次显示的最多字符数。 用Text Field构建UI 一般地, TextField对象被用来创建几个文本框。  Figure 8-2中的应用显示了三个文本框并且处理用户在它们当中输入的数据。 Figure 8-2 TextFieldSample Application Description of "Figure 8-2 TextFieldSample Application" Example 8-2 中的代码块创建了三个文本框和两个按钮,并把使用GridPane 容器他们加入到应用的屏幕上。当你要为你的UI控件实现灵活的布局时这个容器相当方便。 Example 8-2 Adding Text Fields to the Application 复制代码 1. //Creating a GridPane container 2. GridPane grid = new GridPane(); 3. grid.setPadding(new Insets(10, 10, 10, 10)); 4. grid.setVgap(5); 5. grid.setHgap(5); 6. //Defining the Name text field 7. final TextField name = new TextField(); 8. name.setPromptText("Enter your first name."); 9. name.setPrefColumnCount(10); 10. name.getText(); 11. GridPane.setConstraints(name, 0, 0); 12. grid.getChildren().add(name); 13. //Defining the Last Name text field 14. final TextField lastName = new TextField(); 15. lastName.setPromptText("Enter your last name."); 16. GridPane.setConstraints(lastName, 0, 1); 17. grid.getChildren().add(lastName); 18. //Defining the Comment text field 19. final TextField comment = new TextField(); 20. comment.setPrefColumnCount(15); 21. comment.setPromptText("Enter your comment."); 22. GridPane.setConstraints(comment, 0, 2); 23. grid.getChildren().add(comment); 24. //Defining the Submit button 25. Button submit = new Button("Submit"); 26. GridPane.setConstraints(submit, 1, 0); 27. grid.getChildren().add(submit); 28. //Defining the Clear button 29. Button clear = new Button("Clear"); 30. GridPane.setConstraints(clear, 1, 1); 31. grid.getChildren().add(clear); 花 点时间来研究下这块代码。 name, lastName, 和comment文本框使用了TextField 类的空构造方法来创建。和 Example 8-1不同,这里文本框没有使用标签,而是使用提示语提醒用户在文本框中要输入什么类型的数据。setPromptText方法定义了当应用启动后显示在 文本框中的字符串。把 Example 8-2 中的代码加入到应用中,运行效果如 Figure 8-3. Figure 8-3 Three Text Fields with the Prompt Messages Description of "Figure 8-3 Three Text Fields with the Prompt Messages" 文本框中的提示语和文本的区别是提示语不能通过getText方法获得。 实际应用中,文本框中输入的文本是根据特定的业务任务决定的应用逻辑来处理的。 下一部分解释了如何使用文本框处理用户输入并向用户反馈。 处理Text Field数据 前面提到,用户输入文本框的内容能通过TextInput 类的getText方法获得。 研究Example 8-3 中的代码学习怎么处理TextField对象的数据。 Example 8-3 Defining Actions for the Submit and Clear Buttons 复制代码 1. //Adding a Label 2. final Label label = new Label(); 3. GridPane.setConstraints(label, 0, 3); 4. GridPane.setColumnSpan(label, 2); 5. grid.getChildren().add(label); 6. 7. //Setting an action for the Submit button 8. submit.setOnAction(new EventHandler() { 9. 10. @Override 11.     public void handle(ActionEvent e) { 12.         if ((comment.getText() != null && !comment.getText().isEmpty())) { 13.             label.setText(name.getText() + " " + lastName.getText() + ", " 14.                 + "thank you for your comment!"); 15.         } else { 16.             label.setText("You have not left a comment."); 17.         } 18.      } 19. }); 20. 21. //Setting an action for the Clear button 22. clear.setOnAction(new EventHandler() { 23. 24. @Override 25.     public void handle(ActionEvent e) { 26.         name.setText(""); 27.         lastName.setText(""); 28.         comment.setText(""); 29.         label.setText(null); 30.     } 31. }); GridPane容器中的Label控件用来显示应用对用户的回应。当用户点击Submit按钮时,setOnAction方法检查comment文本框。如果它是非空字符串,一条感谢信息就显示出来。否则,应用会提醒用户还没有添加评论。见 Figure 8-4. Figure 8-4 The Comment Text Field Left Blank Description of "Figure 8-4 The Comment Text Field Left Blank" 当用户点击Clear按钮时,三个文本框的内容都将被清除。 .回顾一下你可能用到的文本框使用函数。 copy()– 将当前选择的文本范围转移到剪贴板,保留选择文本。 cut()– 将当前选择的文本范围转移到剪贴板,删除选择文本。 paste()– 将剪贴板内容转移到文本中,取代当前选择文本。 第十五回 JavaFX2.0 密码框PasswordField PasswordField 类实现了一种特定的文本框:用户向其中输入的字符都被隐藏,代之显示的是特殊的回显串。Figure 9-1 是一个带有提示语的密码框。 Figure 9-1 Password Field with a Prompt Message Description of "Figure 9-1 Password Field with a Prompt Message" 创建Password Field 一个入门级的任务是创建一个密码框,见Example 9-1 . Example 9-1 Creating a Password Field PasswordField passwordField = new PasswordField(); passwordField.setPromptText("Your password"); 由 于是用户接口,所以最好给密码框配上一条提示语或者是一个指示标签。和 TextField   类相同,PasswordField类也提供了 setText 方法来设置在应用启动后要在控件中显示的字符串。但是,setText 方法指定的字符串被密码框的回显字符覆盖了。默认地,密码框的回显字符是星号。Figure 9-2 是带有预定义文本的密码框。 Figure 9-2 Password Field with the Set Text Description of "Figure 9-2 Password Field with the Set Text" 密码框中的值也可以用getText 方法获得。您可以在您的应用程序处理这个值,并设置适当的身份验证逻辑。 处理Password的值 花点时间看Example 9-2 中的代码,它实现了一个密码框。 Example 9-2 Implementing the Authentication Logic 复制代码 1. final Label message = new Label(""); 2. 3. VBox vb = new VBox(); 4. vb.setPadding(new Insets(10, 0, 0, 10)); 5. vb.setSpacing(10); 6. HBox hb = new HBox(); 7. hb.setSpacing(10); 8. hb.setAlignment(Pos.CENTER_LEFT); 9. 10. Label label = new Label("Password"); 11. final PasswordField pb = new PasswordField(); 12. 13. pb.setOnAction(new EventHandler() { 14.     @Override public void handle(ActionEvent e) { 15.         if (!pb.getText().equals("T2f$Ay!")) { 16.             message.setText("Your password is incorrect!"); 17.             message.setTextFill(Color.rgb(210, 39, 30)); 18.         } else { 19.             message.setText("Your password has been confirmed"); 20.             message.setTextFill(Color.rgb(21, 117, 84)); 21.         } 22.         pb.setText(" "); 23.     } 24. }); 25. 26. hb.getChildren().addAll(label, pb); 27. vb.getChildren().addAll(hb, message); 这里通过setOnAction 方法为密码框定义了身份验证逻辑。该方法调用的时间是提交密码后,它新建了一个EventHandler 对象来处理键入的值。如果输入值和要求值不同,相应的信息就以红色显示出来,见Figure 9-3 . Figure 9-3 Password is Incorrect Description of "Figure 9-3 Password is Incorrect" 如果输入值符合要求,确认信息就显示出来,见Figure 9-4 . Figure 9-4 Password is Correct Description of "Figure 9-4 Password is Correct" 出于安全考虑,最佳实践是键入文本后清空密码框。在Example 9-2 中,身份验证后会将密码框置空 。 第十六回 JavaFX2.0滚动栏ScrollBar ScrollBar 类使你可以创建滚动栏面板和视图。Figure 10-1 展示了滚动栏的三部分:滚动条(thumb),左右按钮(或上下按钮),可滚动区。 Figure 10-1 Elements of the scroll bar 创建Scroll Bar 花点时间看下 Example 10-1 中的代码块。 Example 10-1 Simple Scroll Bar ScrollBar sc = new ScrollBar(); sc.setMin(0); sc.setMax(100); sc.setValue(50); setMin 和setMax 方法定义了滚动栏呈现的最小值和最大值。当用户移动滚动条时,它的值会改变。在 Example 10-1 中,它的值是50,所以应用启动后滚动条位于滚动栏的中间。默认滚动栏是水平方向的,你要用setOrientation 方法把它设为垂直方向。 用户可以点击左右按钮(垂直滚动栏是上下按钮)按照增长单位来改变值。UNIT_INCREMENT属性指定了当点击按钮时滚动条调整的幅度。也可以点击滚动区来大幅改变。 BLOCK_INCREMENT属性定义了当点击滚动区时改变的幅度。 在您的应用程序,您可以使用几个滚动条来滚动超出可用空间边界的图形内容。 在应用中使用Scroll Bar 实战体验下。  Example 10-2 中创建的应用实现了一个可滚动的视窗来查看图片。这个应用的目的是让用户能够查看垂直盒子(vertical box)中的内容,它们比视窗好高度要长。 Example 10-2 Scrolling Through Multiple Images 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.geometry.Orientation; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.ScrollBar; 8. import javafx.scene.effect.DropShadow; 9. import javafx.scene.image.Image; 10. import javafx.scene.image.ImageView; 11. import javafx.scene.layout.VBox; 12. import javafx.scene.paint.Color; 13. import javafx.stage.Stage; 14. 15. public class Main extends Application { 16. 17.     final ScrollBar sc = new ScrollBar(); 18.     final Image[] images = new Image[5]; 19.     final ImageView[] pics = new ImageView[5]; 20.     final VBox vb = new VBox(); 21.     DropShadow shadow = new DropShadow(); 22. 23.     @Override 24.     public void start(Stage stage) { 25.         Group root = new Group(); 26.         Scene scene = new Scene(root, 180, 180); 27.         scene.setFill(Color.BLACK); 28.         stage.setScene(scene); 29.         stage.setTitle("Scrollbar"); 30.         root.getChildren().addAll(vb, sc); 31. 32.         shadow.setColor(Color.GREY); 33.         shadow.setOffsetX(2); 34.         shadow.setOffsetY(2); 35. 36.         vb.setLayoutX(5); 37.         vb.setSpacing(10); 38. 39.         sc.setLayoutX(scene.getWidth()-sc.getWidth()); 40.         sc.setMin(0); 41.         sc.setOrientation(Orientation.VERTICAL); 42.         sc.setPrefHeight(180); 43.         sc.setMax(360); 44. 45.         for (int i = 0; i < 5; i++) { 46.             final Image image = images[i] = 47.                 new Image(getClass().getResourceAsStream( 48.                     "fw" +(i+1)+ ".jpg") 49.                 ); 50.             final ImageView pic = pics[i] = 51.                 new ImageView(images[i]); 52.             pic.setEffect(shadow); 53.             vb.getChildren().add(pics[i]); 54.         } 55. 56.         sc.valueProperty().addListener(new ChangeListener() { 57.             public void changed(ObservableValue ov, 58.                 Number old_val, Number new_val) { 59.                     vb.setLayoutY(-new_val.doubleValue()); 60.             } 61.         }); 62. 63.         stage.show(); 64.     } 65. 66.     public static void main(String[] args) { 67.         launch(args); 68.     } 69. } 代码的第一行向场景中增加了一个带有图片的垂直盒子和一个滚动栏。 垂直盒子的Y坐标会随着滚动条 VALUE 属性的变化而变化。 所以每次滚动条移动时(或者按钮、滚动区)被点击时,垂直盒子就跟着动。见Example 10-3 . Example 10-3 Implementing the Scrolling of the Vertical Box 复制代码 1. sc.valueProperty().addListener(new ChangeListener() { 2.     public void changed(ObservableValue ov, 3.         Number old_val, Number new_val) { 4.             vb.setLayoutY(-new_val.doubleValue()); 5.         } 6. }); 编译并运行应用,效果如Figure 10-2 . Figure 10-2 Scroll Bar Sample 这个应用是ScrollBar 类的典型应用。你也可以在场景内用定制化的该类创建滚动区域。因为对于每个UI控件和节点(node,有Node这个类的),滚动栏都可以从默认实现中修改外观。 第十七回 JavaFX2.0 滚动窗Scroll Pane 滚动窗为UI元素提供了一个可以滚动查看的视图。该控件让用户可以通过移动视口或者滚动条来查看。Figure 11-1 是一个添加了图片的默认设置的滚动窗。 Figure 11-1 Scroll Pane 创建Scroll Pane Example 11-1 演示了如何在应用中创建滚动窗。 Example 11-1 Using a Scroll Pane to View an Image Image roses = new Image(getClass().getResourceAsStream("roses.jpg")); ScrollPane sp = new ScrollPane(); sp.setNode(new ImageView(roses)); setNode 方法定义了滚动窗的结点是什么内容,可以只指定一个结点。要创建具有多个组件的滚动窗,得用布局容器或者Group类。可以为 setPannable 方法设置true值,这样当点击和移动鼠标时能预览图像,滚动条的位置也会相应改变。 为Scroll Pane设置滚动条策略 ScrollPane 类提供了一种策略来决定何时显示滚动条:总是、从不、需要时( always, never,needed)。分别使用 setHbarPolicy 和setVbarPolicy 方法为水平滚动条和垂直滚动条指定策略。这样,Example 11-2 中的垂直滚动条会一直显示,而水平的不会显示。 Example 11-2 Setting the Horizontal and Vertical Scroll Bar Policies sp.setHbarPolicy(ScrollBarPolicy.NEVER); sp.setVbarPolicy(ScrollBarPolicy.ALWAYS); 结果是只能垂直地滚动图片,见 Figure 11-2 . Figure 11-2 Disabling the Horizontal Scroll Bar 改变Scroll Pane中组件的大小 设计UI接口时可能需要能够改变组件的大小已让它们适合滚动窗的宽和高。为 setFitToWidth 或 setFitToHeight 方法设置true 值来匹配特定的方向。 Figure 11-3 中的滚动窗包含单选按钮、文本框和密码框。这些内容的大小超过了滚动窗预先定义的尺寸,所以垂直滚动条显示了出来。然而,由于setFitToWidth 方法被设为true,视窗宽度会伸缩使水平方向无滚动条。 Figure 11-3 Fitting the Width of the Scroll Pane 默认FIT_TO_WIDTH 和 FIT_TO_HEIGHT 属性都是false,可改变大小的内容也保持原始大小。从上面代码移除 setFitToWidth 方法后,显示如Figure 11-4 . Figure 11-4 Default Properties for Fitting the Content ScrollPane 类可以取回和设置内容在水平和垂直方向的当前、最小、最大值。学习怎么使用吧 使用Scroll Pane的样例程序 Example 11-3 使用滚动窗显示一个带有图片的垂直盒子。ScrollPane 类的VVALUE属性帮助辨识当然显示的图片并显示它的名称。 Example 11-3 Using a Scroll Pane to View Images 复制代码 1. package scrollpanesample; 2. 3. import javafx.application.Application; 4. import javafx.beans.value.ChangeListener; 5. import javafx.beans.value.ObservableValue; 6. import javafx.scene.Scene; 7. import javafx.scene.control.Label; 8. import javafx.scene.control.ScrollPane; 9. import javafx.scene.image.Image; 10. import javafx.scene.image.ImageView; 11. import javafx.scene.layout.Priority; 12. import javafx.scene.layout.VBox; 13. import javafx.stage.Stage; 14. 15. public class Main extends Application { 16. 17.     final ScrollPane sp = new ScrollPane(); 18.     final Image[] images = new Image[5]; 19.     final ImageView[] pics = new ImageView[5]; 20.     final VBox vb = new VBox(); 21.     final Label fileName = new Label(); 22.     final String [] imageNames = new String [] {"fw1.jpg", "fw2.jpg", 23.         "fw3.jpg", "fw4.jpg", "fw5.jpg"}; 24. 25.     @Override 26.     public void start(Stage stage) { 27.         VBox box = new VBox(); 28.         Scene scene = new Scene(box, 180, 180); 29.         stage.setScene(scene); 30.         stage.setTitle("Scroll Pane"); 31.         box.getChildren().addAll(sp, fileName); 32.         VBox.setVgrow(sp, Priority.ALWAYS); 33. 34.         fileName.setLayoutX(30); 35.         fileName.setLayoutY(160); 36. 37.         for (int i = 0; i < 5; i++) { 38.             images[i] = new Image(getClass().getResourceAsStream(imageNames[i])); 39.             pics[i] = new ImageView(images[i]); 40.             pics[i].setFitWidth(100); 41.             pics[i].setPreserveRatio(true); 42.             vb.getChildren().add(pics[i]); 43.         } 44. 45.         sp.setVmax(440); 46.         sp.setPrefSize(115, 150); 47.         sp.setContent(vb); 48.         sp.vvalueProperty().addListener(new ChangeListener() { 49.             public void changed(ObservableValue ov, 50.                 Number old_val, Number new_val) { 51.                     fileName.setText(imageNames[(new_val.intValue() - 1)/100]); 52.             } 53.         }); 54.         stage.show(); 55.     } 56. 57.     public static void main(String[] args) { 58.         launch(args); 59.     } 60. } Figure 11-5 是编译并运行的效果。 Figure 11-5 Scrolling Images 垂直滚动条的最大值等于垂直盒子的高度。 Example 11-4 中的代码块显示了当前显示图片的名称。 Example 11-4 Tracking the Change of the Scroll Pane's Vertical Value sp.vvalueProperty().addListener(new ChangeListener() {     public void changed(ObservableValue ov,         Number old_val, Number new_val) {             fileName.setText(imageNames[(new_val.intValue() - 1)/100]);         } }); ImageView对象限制了图片高度是100点。所以, new_val.intValue() - 1 除以100的结果给出了当前图片的索引。 可以在应用中改变水平滚动条的最小值和最大值来动态更新用户接口。 第十八回 JavaFX2.0 列表框ListView ListView类呈现一个可滚动的项目列表。 Figure 12-1 展示了一个住宿类型列表。 Figure 12-1 Simple List View 可以通过setItems 方法定义项目来产生列表。也可以使用setCellFactory 方法为列表中项目创建一个视图。 创建List View Example 12-1中的代码块实现了Figure 12-1 中带有String 类项目的列表。 Example 12-1 Creating a List View Control ListView list = new ListView(); ObservableList items =FXCollections.observableArrayList (     "Single", "Double", "Suite", "Family App"); list.setItems(items); 使用setPrefHeight 和setPrefWidth 方法来改变列表视图控件的大小和高度。Example 12-2限制垂直列表具有100点宽度和70点高度,效果见Figure 12-2。 Example 12-2 Setting Height and Width for a List View list.setPrefWidth(100); list.setPrefHeight(70); Figure 12-2 Resized Vertical List Description of "Figure 12-2 Resized Vertical List" 要 将ListView对象设置为水平方向的可以通过将方向属性设为 Orientation.HORIZONTAL ,这样做即可:list.setOrientation(Orientation.HORIZONTAL) 。 Figure 12-1 和Figure 12-3 中的水平列表具有相同的项目。 Figure 12-3 Horizontal List View Control Description of "Figure 12-3 Horizontal List View Control" 可以用下面的组合方法获得每个项目当前的状态: getSelectionModel().selectedIndexProperty() – 返回当前被选中项目的索引。 getSelectionModel().selectedItemProperty() – 返回当前被选中项目。 getFocusModel().getFocusedIndex() – 返回当前有焦点的项目索引。 getFocusModel().getFocusedItem() – 返回当前有焦点的项目。 注意,选中的和有焦点的项目都是只读的,应用启动后是不能为项目指定这些属性的。 前面的代码样例讲解了怎么创建具有文本项目的列表。然而,列表视图控件可以包含Node对象。 用数据产生List View 研究下面的代码学习怎么用细胞工厂(cell factory)产生列表项目。Example 12-3 中的应用创建了一个颜色模式列表。 Example 12-3 Creating a Cell Factory 复制代码 1. import javafx.application.Application; 2. import javafx.collections.FXCollections; 3. import javafx.collections.ObservableList; 4. import javafx.scene.Scene; 5. import javafx.scene.control.ListCell; 6. import javafx.scene.control.ListView; 7. import javafx.scene.layout.Priority; 8. import javafx.scene.layout.VBox; 9. import javafx.scene.paint.Color; 10. import javafx.scene.shape.Rectangle; 11. import javafx.stage.Stage; 12. import javafx.util.Callback; 13. public class Main extends Application { 14. 15.     ListView list = new ListView(); 16.     ObservableList data = FXCollections.observableArrayList( 17.             "chocolate", "salmon", "gold", "coral", "darkorchid", 18.             "darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue", 19.             "blueviolet", "brown"); 20. 21.     @Override 22.     public void start(Stage stage) { 23.         VBox box = new VBox(); 24.         Scene scene = new Scene(box, 200, 200); 25.         stage.setScene(scene); 26.         stage.setTitle("ListViewSample"); 27.         box.getChildren().addAll(list); 28.         VBox.setVgrow(list, Priority.ALWAYS); 29. 30.         list.setItems(data); 31. 32.         list.setCellFactory(new Callback, ListCell>() { 33.             @Override public ListCell call(ListView list) { 34.                 return new ColorRectCell(); 35.             } 36.         }); 37. 38.         stage.show(); 39.     } 40.      41.     static class ColorRectCell extends ListCell { 42.         @Override 43.         public void updateItem(String item, boolean empty) { 44.             super.updateItem(item, empty); 45.             Rectangle rect = new Rectangle(100, 20); 46.             if (item != null) { 47.                 rect.setFill(Color.web(item)); 48.                 setGraphic(rect); 49.             } 50.         } 51.     } 52.      53.     public static void main(String[] args) { 54.         launch(args); 55.     } 56. } 细胞工厂产生了 ListCell 对象。每个细胞都关联一个单一的数据项目并显示列表中视图的一“行”。细胞呈现的内容通过setGraphic方法可以包含其他控件、文本、形状、图像。该应用中,列表细胞放的是矩形。 Figure 12-4 是该应用编译运行后产生的效果。 Figure 12-4 List of Color Patterns 你可以滚动列表,选择或取消选择项目,也可以扩展应用来用颜色填充文本标签。 处理选中的List Item 按照 Example 12-4 修改应用的代码,以使其能处理特定项目被选中的事件。 Example 12-4 Processing Events for a List Item 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.collections.FXCollections; 5. import javafx.collections.ObservableList; 6. import javafx.scene.Scene; 7. import javafx.scene.control.Label; 8. import javafx.scene.control.ListCell; 9. import javafx.scene.control.ListView; 10. import javafx.scene.layout.Priority; 11. import javafx.scene.layout.VBox; 12. import javafx.scene.paint.Color; 13. import javafx.scene.shape.Rectangle; 14. import javafx.scene.text.Font; 15. import javafx.stage.Stage; 16. import javafx.util.Callback; 17. 18. public class Main extends Application { 19. 20.     ListView list = new ListView(); 21.     ObservableList data = FXCollections.observableArrayList( 22.             "chocolate", "salmon", "gold", "coral", "darkorchid", 23.             "darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue", 24.             "blueviolet", "brown"); 25.     final Label label = new Label(); 26. 27. 28. 29. 30. 31. 32.     @Override 33.     public void start(Stage stage) { 34.         VBox box = new VBox(); 35.         Scene scene = new Scene(box, 200, 200); 36.         stage.setScene(scene); 37.         stage.setTitle("ListViewSample"); 38.         box.getChildren().addAll(list, label); 39.         VBox.setVgrow(list, Priority.ALWAYS); 40. 41.         label.setLayoutX(10); 42.         label.setLayoutY(115); 43.         label.setFont(Font.font("Verdana", 20)); 44. 45.         list.setItems(data); 46. 47.         list.setCellFactory(new Callback, ListCell>() { 48.             @Override public ListCell call(ListView list) { 49.                 return new ColorRectCell(); 50.             } 51.         }); 52. 53.         list.getSelectionModel().selectedItemProperty().addListener( 54. 55.             new ChangeListener() { 56. 57.                 public void changed(ObservableValue ov, 58. 59.                     String old_val, String new_val) { 60. 61.                         label.setText(new_val); 62. 63.                         label.setTextFill(Color.web(new_val)); 64. 65.             } 66. 67.         }); 68. 69.         stage.show(); 70.     } 71.      72.     static class ColorRectCell extends ListCell { 73.         @Override 74.         public void updateItem(String item, boolean empty) { 75.             super.updateItem(item, empty); 76.             Rectangle rect = new Rectangle(100, 20); 77.             if (item != null) { 78.                 rect.setFill(Color.web(item)); 79.                 setGraphic(rect); 80.             } 81.         } 82.     } 83.      84.     public static void main(String[] args) { 85.         launch(args); 86.     } 87. } addListener 方法调用后为 selectedItemProperty 新建了一个ChangeListener 对象来绑定选中项目的改变。比如说,深紫色项目被选中了,标签接收到了"darkorchid"标题并用相应的颜色填充。修改后应用的效果见Figure 12-5 . Figure 12-5 Selecting a Dark Orchid Color Pattern 第二十回 JavaFX2.0 分割线Separator JavaFX API中的 Separator类呈现的是一条水平或者垂直的分隔线。它是用来分隔应用中用户接口的元素的,并不提供任何行为 创建Separator Example 14-1 中的代码块创建了一条水平的一条垂直的分隔线。 Example 14-1 Vertical and Horizontal Separators 复制代码 1. //Horizontal separator 2. Separator separator1 = new Separator(); 3. //Vertical separator 4. Separator separator2 = new Separator(); 5. separator2.setOrientation(Orientation.VERTICAL); Separator 类继承了Node类,所以,分隔线就继承了 Node类的所有常量和变量。 一般分隔线是用来分隔UI控件成组的。研究下Example 14-2 中的代码,它将春天的月份和夏天的分开了 Example 14-2 Using a Separator Between Checkbox Categories 复制代码 1. final String[] names = new String[]{"March", "April", "May", 2.     "June", "July", "August"}; 3. final CheckBox[] cbs = new CheckBox[names.length]; 4. final Separator separator = new Separator(); 5. final VBox vbox = new VBox(); 6. 7. for (int i = 0; i < names.length; i++) { 8.     cbs[i] = new CheckBox(names[i]); 9. } 10.                          11. separator.setMaxWidth(40); 12. separator.setAlignment(Pos.CENTER_LEFT); 13. vbox.getChildren().addAll(cbs); 14. vbox.setSpacing(5); 15. vbox.getChildren().add(3, separator); 运行效果见Figure 14-1 . Figure 14-1 Checkboxes and a Separator 分隔线会占据分配给它的全部水平或垂直空间。setMaxWidth方法用来指定一个特定的宽度, setValignment 方法指定了分隔线在其分配的布局空间内的垂直位置。类似地,也可以使用setHalignment 方法来设置其水平位置。 在Example 14-2 中,分隔线被使用专用的方法add(index, node)加入了垂直盒子。可以使用这种方法在创建UI后或者动态改变UI时包括分隔线。 为UI添加分隔线 前面提到,分隔线可以将UI控件分组。可以使用它们构建用户接口,就像下面这样显示天气预报Figure 14-2 . Figure 14-2 Structuring Weather Forecast Data with Separators Description of "Figure 14-2 Structuring Weather Forecast Data with Separators" Figure 14-2 中的效果是,分隔线用来分开Label和 ImageView对象。研究下下面的代码 Example 14-3 . Example 14-3 Using Separators in a Weather Forecast Application 复制代码 1. import javafx.application.Application; 2. import javafx.geometry.Insets; 3. import javafx.geometry.Orientation; 4. import javafx.geometry.VPos; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.*; 8. import javafx.scene.image.Image; 9. import javafx.scene.image.ImageView; 10. import javafx.scene.layout.GridPane; 11. import javafx.scene.text.Font; 12. import javafx.stage.Stage; 13. 14. public class Main extends Application { 15. 16.     Label caption = new Label("Weather Forecast"); 17.     Label friday = new Label("Friday"); 18.     Label saturday = new Label("Saturday"); 19.     Label sunday = new Label("Sunday"); 20. 21.     @Override 22.     public void start(Stage stage) { 23.         Group root = new Group(); 24.         Scene scene = new Scene(root, 500, 300); 25.         stage.setScene(scene); 26.         stage.setTitle("Separator Sample"); 27. 28.         GridPane grid = new GridPane(); 29.         grid.setPadding(new Insets(10, 10, 10, 10)); 30.         grid.setVgap(2); 31.         grid.setHgap(5); 32. 33.         scene.setRoot(grid); 34. 35.         Image cloudImage = new Image(getClass().getResourceAsStream("cloud.jpg")); 36.         Image sunImage = new Image(getClass().getResourceAsStream("sun.jpg")); 37. 38.         caption.setFont(Font.font("Verdana", 20)); 39.         GridPane.setConstraints(caption, 0, 0); 40.         GridPane.setColumnSpan(caption, 8); 41.         grid.getChildren().add(caption); 42. 43.         final Separator sepHor = new Separator(); 44.         sepHor.setValignment(VPos.CENTER); 45.         GridPane.setConstraints(sepHor, 0, 1); 46.         GridPane.setColumnSpan(sepHor, 7); 47.         grid.getChildren().add(sepHor); 48. 49.         friday.setFont(Font.font("Verdana", 18)); 50.         GridPane.setConstraints(friday, 0, 2); 51.         GridPane.setColumnSpan(friday, 2); 52.         grid.getChildren().add(friday); 53. 54.         final Separator sepVert1 = new Separator(); 55.         sepVert1.setOrientation(Orientation.VERTICAL); 56.         sepVert1.setValignment(VPos.CENTER); 57.         sepVert1.setPrefHeight(80); 58.         GridPane.setConstraints(sepVert1, 2, 2); 59.         GridPane.setRowSpan(sepVert1, 2); 60.         grid.getChildren().add(sepVert1); 61. 62.         saturday.setFont(Font.font("Verdana", 18)); 63.         GridPane.setConstraints(saturday, 3, 2); 64.         GridPane.setColumnSpan(saturday, 2); 65.         grid.getChildren().add(saturday); 66. 67.         final Separator sepVert2 = new Separator(); 68.         sepVert2.setOrientation(Orientation.VERTICAL); 69.         sepVert2.setValignment(VPos.CENTER); 70.         sepVert2.setPrefHeight(80); 71.         GridPane.setConstraints(sepVert2, 5, 2); 72.         GridPane.setRowSpan(sepVert2, 2); 73.         grid.getChildren().add(sepVert2); 74. 75.         sunday.setFont(Font.font("Verdana", 18)); 76.         GridPane.setConstraints(sunday, 6, 2); 77.         GridPane.setColumnSpan(sunday, 2); 78.         grid.getChildren().add(sunday); 79. 80.         final ImageView cloud = new ImageView(cloudImage); 81.         GridPane.setConstraints(cloud, 0, 3); 82.         grid.getChildren().add(cloud); 83. 84.         final Label t1 = new Label("16"); 85.         t1.setFont(Font.font("Verdana", 20)); 86.         GridPane.setConstraints(t1, 1, 3); 87.         grid.getChildren().add(t1); 88. 89.         final ImageView sun1 = new ImageView(sunImage); 90.         GridPane.setConstraints(sun1, 3, 3); 91.         grid.getChildren().add(sun1); 92. 93.         final Label t2 = new Label("18"); 94.         t2.setFont(Font.font("Verdana", 20)); 95.         GridPane.setConstraints(t2, 4, 3); 96.         grid.getChildren().add(t2); 97. 98.         final ImageView sun2 = new ImageView(sunImage); 99.         GridPane.setConstraints(sun2, 6, 3); 100.         grid.getChildren().add(sun2); 101. 102.         final Label t3 = new Label("20"); 103.         t3.setFont(Font.font("Verdana", 20)); 104.         GridPane.setConstraints(t3, 7, 3); 105.         grid.getChildren().add(t3); 106. 107.         stage.show(); 108.     } 109.     public static void main(String[] args) { 110.         launch(args); 111.     } 112. } 该应用使用了水平和垂直分隔线,并在 GridPane容器中跨了行和列。 也可以为分隔线设置优先的长度(水平分隔线的宽度和垂直分隔线的高度)这样界面大小改变时就能动态伸缩了。可以为Separator 对象应用CSS来改变分隔线的视觉外观。 美化Separator 要为 Example 14-3 中的分隔线应用相同的效果,可以创建CSS文件(例如controlStyle.css) 并保存在应用主类的相同包中。Example 14-4 是一些可以加入到controlStyle文件的CSS类。 Example 14-4 Using CSS Classes to Style Separators /*controlStyle.css */ .separator{     -fx-background-color: #e79423;     -fx-background-radius: 2; } 可以使用 Scene 类的getStylesheets 方法来应用分隔线风格,见 Example 14-5 . Example 14-5 Enabling Style Sheets in a JavaFX Application scene.getStylesheets().add("separatorsample/controlStyle.css"); Figure 14-3 是应用修改后运行时分隔线的效果。 Figure 14-3 Styled Separators 第二十一回 JavaFX2.0 滑动条Slider Slider 类呈现一个控件来显示和回应一个范围的数值。该控件包括一个轨道和一个可以拖动的滑标,也包含刻度和刻度标记来指示数值。Figure 15-1 展示了一个滑动条并指明了其主要元素。 Figure 15-1 Elements of a Slider Description of "Figure 15-1 Elements of a Slider" 创建Slider 花点时间看下 Example 15-1 中的代码,它产生了一个Figure 15-1 展示的滑动条。 Example 15-1 Creating a Slider 复制代码 1. Slider slider = new Slider(); 2. slider.setMin(0); 3. slider.setMax(100); 4. slider.setValue(40); 5. slider.setShowTickLabels(true); 6. slider.setShowTickMarks(true); 7. slider.setMajorTickUnit(50); 8. slider.setMinorTickCount(5); 9. slider.setBlockIncrement(10); setMin 和 setMax 方法分布定义了滑动条呈现的最小值和最大值。setValue方法指定了滑动条的当前值,当然必须在最小值和最大值之间。 使用该方法来定义应用启动后滑标的位置。 两 个布尔方法 setShowTickMarks 和 setShowTickLabels 定义了滑动条的视觉外观。在 Example 15-1 中,刻度和数值显示了。另外,大刻度之间的单元距离设置为50,大刻度直接的小刻度数量定义为5。把 setSnapToTicks方法设为 true 来保持滑标总和刻度对其。 setBlockIncrement方法定义了用户点击轨道时滑标移动的距离。 Example 15-1 中该值是10,就是说当用户点击轨道时,滑标会向点击方向移动10单位。 在图形应用中使用滑动条 现在看一下Figure 15-2。 该应用使用了3个滑动条来白哦几图片的属性。每个滑动条调整一个特定的视觉特点:透明度、褐色调、伸缩比例。 Figure 15-2 Three Sliders Description of "Figure 15-2 Three Sliders " Example 15-2   shows the source code of this application. Example 15-2 Slider Sample 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.geometry.Insets; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.Label; 8. import javafx.scene.control.Slider; 9. import javafx.scene.effect.SepiaTone; 10. import javafx.scene.image.Image; 11. import javafx.scene.image.ImageView; 12. import javafx.scene.layout.GridPane; 13. import javafx.scene.paint.Color; 14. import javafx.stage.Stage; 15. 16. public class Main extends Application { 17. 18.     final Slider opacityLevel = new Slider(0, 1, 1);     19.     final Slider sepiaTone = new Slider(0, 1, 1); 20.     final Slider scaling = new Slider (0.5, 1, 1); 21.     final Image image  = new Image(getClass().getResourceAsStream( 22.         "cappuccino.jpg") 23.     ); 24. 25.     final Label opacityCaption = new Label("Opacity Level:"); 26.     final Label sepiaCaption = new Label("Sepia Tone:"); 27.     final Label scalingCaption = new Label("Scaling Factor:"); 28. 29.     final Label opacityValue = new Label( 30.         Double.toString(opacityLevel.getValue())); 31.     final Label sepiaValue = new Label( 32.         Double.toString(sepiaTone.getValue())); 33.     final Label scalingValue = new Label( 34.         Double.toString(scaling.getValue())); 35. 36.     final static Color textColor = Color.WHITE; 37.     final static SepiaTone sepiaEffect = new SepiaTone(); 38. 39.     @Override 40.     public void start(Stage stage) { 41.         Group root = new Group(); 42.         Scene scene = new Scene(root, 600, 400); 43.         stage.setScene(scene); 44.         stage.setTitle("Slider Sample"); 45.         scene.setFill(Color.BLACK); 46. 47.         GridPane grid = new GridPane(); 48.         grid.setPadding(new Insets(10, 10, 10, 10)); 49.         grid.setVgap(10); 50.         grid.setHgap(70); 51. 52.         final ImageView cappuccino = new ImageView (image); 53.         cappuccino.setEffect(sepiaEffect); 54.         GridPane.setConstraints(cappuccino, 0, 0); 55.         GridPane.setColumnSpan(cappuccino, 3); 56.         grid.getChildren().add(cappuccino); 57.         scene.setRoot(grid); 58. 59.         opacityCaption.setTextFill(textColor); 60.         GridPane.setConstraints(opacityCaption, 0, 1); 61.         grid.getChildren().add(opacityCaption); 62.          63. 64.         opacityLevel.valueProperty().addListener(new ChangeListener() { 65.             public void changed(ObservableValue ov, 66.                 Number old_val, Number new_val) { 67.                     cappuccino.setOpacity(new_val.doubleValue()); 68.                     opacityValue.setText(String.format("%.2f", new_val)); 69.             } 70.         }); 71. 72.         GridPane.setConstraints(opacityLevel, 1, 1); 73.         grid.getChildren().add(opacityLevel); 74. 75.         opacityValue.setTextFill(textColor); 76.         GridPane.setConstraints(opacityValue, 2, 1); 77.         grid.getChildren().add(opacityValue); 78. 79.         sepiaCaption.setTextFill(textColor); 80.         GridPane.setConstraints(sepiaCaption, 0, 2); 81.         grid.getChildren().add(sepiaCaption); 82. 83.         sepiaTone.valueProperty().addListener(new ChangeListener() { 84.             public void changed(ObservableValue ov, 85.                 Number old_val, Number new_val) { 86.                     sepiaEffect.setLevel(new_val.doubleValue()); 87.                     sepiaValue.setText(String.format("%.2f", new_val)); 88.             } 89.         }); 90.         GridPane.setConstraints(sepiaTone, 1, 2); 91.         grid.getChildren().add(sepiaTone); 92. 93.         sepiaValue.setTextFill(textColor); 94.         GridPane.setConstraints(sepiaValue, 2, 2); 95.         grid.getChildren().add(sepiaValue); 96. 97.         scalingCaption.setTextFill(textColor); 98.         GridPane.setConstraints(scalingCaption, 0, 3); 99.         grid.getChildren().add(scalingCaption); 100. 101.         scaling.valueProperty().addListener(new ChangeListener() { 102.             public void changed(ObservableValue ov, 103.                 Number old_val, Number new_val) { 104.                     cappuccino.setScaleX(new_val.doubleValue()); 105.                     cappuccino.setScaleY(new_val.doubleValue()); 106.                     scalingValue.setText(String.format("%.2f", new_val)); 107.             } 108.         }); 109.         GridPane.setConstraints(scaling, 1, 3); 110.         grid.getChildren().add(scaling); 111. 112.         scalingValue.setTextFill(textColor); 113.         GridPane.setConstraints(scalingValue, 2, 3); 114.         grid.getChildren().add(scalingValue); 115.         stage.show(); 116.     } 117. 118.     public static void main(String[] args) { 119.         launch(args); 120.     } 121. } ImageView 对象的不透明度由第一个滑动条改变,叫opacityLevel。SepiaTone 效果的改变由sepiaTone滑动条控制。第三个滑动条定义了放大倍数,调用的是 setScaleX 和 setScaleY 方法。 Example 15-3   中的代码是一个把Slider类的 getValue方法返回的双精度值转换为 String ,也应用了格式来显示滑动条的值:点后两位小数的浮点类型。 Example 15-3 Formatting the Rendered Slider's Value scalingValue.setText((Double.toString(value)).format("%.2f", value)); 下一步就是为它应用视效或CSS风格来改善外观和感觉。 第二十二回 JavaFX2.0 进度条和进度指示器 ProgressIndicator及其直接子类 ProgressBar提供了指示特定任务正在运行并检测其完成进度的能力。 不过ProgressBar类用来显示一个显示进度完成的条,而 ProgressIndicator类则是将进度动态地显示在一个饼图里。见 Figure 16-1 . Figure 16-1 Progress Bar and Progress Indicator Description of "Figure 16-1 Progress Bar and Progress Indicator" 创建进度控件 Example 16-1 中的代码能够在JavaFX应用中插入一个进度控件。 Example 16-1 Implementing the Progress Bar and Progress Indicator 复制代码 1. ProgressBar pb = new ProgressBar(0.6); 2. ProgressIndicator pi = new ProgressIndicator(0.6); 也 可以使用空构造方法创建进度控件而不指定参数。这时候,可以使用setProgress方法为它分配值。 另一个初始化进度控件的方法是使用 ProgressBarBuilder 类,该类包括诸如build和 progress 之类的方法。可以查看API文档去了解更多。 有时候应用并不能缺地in个任务的完成时间,这时进度控件就保持在非确定模式中直到可以确定。Figure 16-2 中是依赖于不同进度变量值的进度控件。 Figure 16-2 Various States of Progress Controls Description of "Figure 16-2 Various States of Progress Controls" Example 16-2   shows the source code of the application shown in  Figure 16-2 . Example 16-2 Enabling Different States of Progress Controls 复制代码 1. import javafx.application.Application; 2. import javafx.geometry.Pos; 3. import javafx.scene.Group; 4. import javafx.scene.Scene; 5. import javafx.scene.control.Label; 6. import javafx.scene.control.ProgressBar; 7. import javafx.scene.control.ProgressIndicator; 8. import javafx.scene.layout.HBox; 9. import javafx.scene.layout.VBox; 10. import javafx.stage.Stage; 11. 12. public class Main extends Application 13. { final Float[] values = new Float[] {-1.0f, 0f, 0.6f, 1.0f}; 14. final Label [] labels = new Label[values.length]; 15. final ProgressBar[] pbs = new ProgressBar[values.length]; 16. final ProgressIndicator[] pins = new ProgressIndicator[values.length]; 17. final HBox hbs [] = new HBox [values.length]; 18. @Override 19. public void start(Stage stage) 20. { Group root = new Group(); 21. Scene scene = new Scene(root, 300, 150); 22. scene.getStylesheets().add("progresssample/Style.css"); 23. stage.setScene(scene); 24. stage.setTitle("Progress Controls"); 25. for (int i = 0; i < values.length; i++) 26. { final Label label = labels[i] = new Label(); 27. label.setText("progress:" + values[i]); 28. final ProgressBar pb = pbs[i] = new ProgressBar(); 29. pb.setProgress(values[i]); 30. final ProgressIndicator pin = pins[i] = new ProgressIndicator(); 31. pin.setProgress(values[i]); 32. final HBox hb = hbs[i] = new HBox(); 33. hb.setSpacing(5); 34. hb.setAlignment(Pos.CENTER); 35. hb.getChildren().addAll(label, pb, pin); 36. } 37. final VBox vb = new VBox(); 38. vb.setSpacing(5); 39. vb.getChildren().addAll(hbs); 40. scene.setRoot(vb); 41. stage.show(); 42. } 43. public static void main(String[] args) 44. { launch(args); } 45. } 一个在0和1之间的正数用来指示进程的百分比。 比如,0.4代表40%。而一个负数表示进度在非确定模式。用方法isIndeterminate 可以检查进度控件是否在非确定模式中。 在界面上指示进度 Figure 16-2 曾经简单的显示了进度控件的所以可能状态。实际应用中,进度值可以通过其他UI元素的值获得。 研究 Example 16-3 中的代码学习如何为基于滑标位置的进度条和指示器设置值。 Example 16-3 Receiving the Progress Value from a Slider 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.geometry.Pos; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.ProgressBar; 8. import javafx.scene.control.ProgressIndicator; 9. import javafx.scene.control.Slider; 10. import javafx.scene.layout.HBox; 11. import javafx.stage.Stage; 12. 13. public class Main  extends Application { 14. 15.     @Override 16.     public void start(Stage stage) { 17.         Group root = new Group(); 18.         Scene scene = new Scene(root); 19.         stage.setScene(scene); 20.         stage.setTitle("Progress Controls"); 21. 22.         final Slider slider = new Slider(); 23.         slider.setMin(0); 24.         slider.setMax(50); 25.          26.         final ProgressBar pb = new ProgressBar(0); 27.         final ProgressIndicator pi = new ProgressIndicator(0); 28. 29.         slider.valueProperty().addListener(new ChangeListener() { 30.             public void changed(ObservableValue ov, 31.                 Number old_val, Number new_val) { 32.                 pb.setProgress(new_val.doubleValue()/50); 33.                 pi.setProgress(new_val.doubleValue()/50); 34.             } 35.         }); 36. 37.         final HBox hb = new HBox(); 38.         hb.setSpacing(5); 39.         hb.setAlignment(Pos.CENTER); 40.         hb.getChildren().addAll(slider, pb, pi); 41.         scene.setRoot(hb); 42.         stage.show(); 43.     } 44.     public static void main(String[] args) { 45.         launch(args); 46.     } 47. } 编译运行效果见 Figure 16-3 . Figure 16-3 Indicating the Progress Set by a Slider Description of "Figure 16-3 Indicating the Progress Set by a Slider" 一个 ChangeListener 对象决定了是否滑动条在动并计算进度条和指示器的值,所以进度控件值的范围是0.0到1.0. 第二十三回 JavaFX2.0 超链接Hyperlink Hyperlink 类呈现的是Labeled 控件的另一种形式,主要用来格式化超链接文本。Figure 17-1 显示了默认超链接的三个实现状态。 Figure 17-1 Three States of a Hyperlink Control 创建Hyperlink 这些代码将产生上面的效果 Example 17-1 . Example 17-1 Typical Hyperlink Hyperlink link = new Hyperlink(); link.setText("http://example.com"); link.setOnAction(new EventHandler() {     @Override     public void handle(ActionEvent e) {         System.out.println("This link is clicked");     } }); setText 实例方法定义了超链接的文本。由于 Hyperlink 类继承了Labeled 类,所以可以为超链接指定特定的字体和内容。setOnAction 方法定义了任何时候点击超链接的行为,和Button控件很像。在 Example 17-1 中,这种行为只是用来打印一个字符串。实际上,它可以实现更多更复杂的认为。 连接到本地内容 Figure 17-2 中的应用显示了本地的图片。 Figure 17-2 Viewing Images Description of "Figure 17-2 Viewing Images" 看下它的代码 Example 17-2 . Example 17-2 Using Hyperlinks to VIew Images 复制代码 1. import javafx.application.Application; 2. import javafx.event.ActionEvent; 3. import javafx.event.EventHandler; 4. import javafx.scene.*; 5. import javafx.scene.control.*; 6. import javafx.scene.image.Image; 7. import javafx.scene.image.ImageView; 8. import javafx.scene.layout.VBox; 9. import javafx.stage.Stage; 10. 11. public class Main extends Application { 12. 13.     final static String[] imageFiles = new String[]{ 14.         "product.png", 15.         "education.png", 16.         "partners.png", 17.         "support.png" 18.     }; 19.     final static String[] captions = new String[]{ 20.         "Products", 21.         "Education", 22.         "Partners", 23.         "Support" 24.     }; 25.     final ImageView selectedImage = new ImageView(); 26.     final ScrollPane list = new ScrollPane(); 27.     final Hyperlink[] hpls = new Hyperlink[captions.length]; 28.     final Image[] images = new Image[imageFiles.length]; 29. 30.     public static void main(String[] args) { 31.         Application.launch(args); 32.     } 33. 34.     @Override 35.     public void start(Stage stage) { 36.         Scene scene = new Scene(new Group()); 37.         stage.setTitle("Hyperlink Sample"); 38.         stage.setWidth(300); 39.         stage.setHeight(200); 40.         selectedImage.setLayoutX(100); 41.         selectedImage.setLayoutY(10); 42. 43.         for (int i = 0; i < captions.length; i++) { 44.             final Hyperlink hpl = hpls[i] = new Hyperlink(captions[i]); 45.             final Image image = images[i] = new Image( 46.                 getClass().getResourceAsStream(imageFiles[i]) 47.             ); 48.             hpl.setOnAction(new EventHandler() { 49.                 @Override 50.                 public void handle(ActionEvent e) { 51.                     selectedImage.setImage(image); 52.                 } 53.             }); 54.         } 55. 56.         final Button button = new Button("Refresh links"); 57.         button.setOnAction(new EventHandler() { 58.                 @Override 59.                 public void handle(ActionEvent e) { 60.                     for (int i = 0; i < captions.length; i++) { 61.                         hpls[i].setVisited(false); 62.                         selectedImage.setImage(null); 63.                     } 64.                 } 65.             }); 66. 67.         VBox vbox = new VBox(); 68.         vbox.getChildren().addAll(hpls); 69.         vbox.getChildren().add(button); 70.         vbox.setSpacing(5); 71. 72.         ((Group) scene.getRoot()).getChildren().addAll(vbox, selectedImage); 73.         stage.setScene(scene); 74.         stage.show(); 75.     } 76. } 该应用在for循环中创建了四个 Hyperlink 对象。点击特点的超链接会调用setOnAction 方法产生不同的行为。这样,images数组中相应的图片就设置给 selectedImage 变量。 当点击一个超链接时,它就成为了访问过的(visited)。可以使用Hyperlink 类的setVisited 方法刷新链接。见Example 17-3 中的代码。 Example 17-3 Refreshing the HyperlInks 复制代码 1. final Button button = new Button("Refresh links"); 2. button.setOnAction(new EventHandler() { 3.     @Override 4.     public void handle(ActionEvent e) { 5.        for (int i = 0; i < captions.length; i++) { 6.            hpls[i].setVisited(false); 7.            selectedImage.setImage(null); 8.        } 9.     } 10. }); 点击Refresh Links按钮就会就把超链接都充值为未访问状态。见Figure 17-3 . Figure 17-3 Unvisited Hyperlinks Description of "Figure 17-3 Unvisited Hyperlinks" 由于Hyperlink类继承了 Labeled 类,所以除了文本还可以为其指定图片。下一部分的应用就使用了文本和图片链接并加载远程HTML页面。 链接到远程内容 可以在JavaFX应用中显示HTML内容,方法是在场景内绑定WebView 浏览器。WebView 组件提供了网页的基本浏览功能。除此之外,还支持用户交互,如导航和执行JavaScript命令。 研究Example 17-4 中的代码,它创建了带有文本和图像的超链接。点击超链接后,相应的值就作为URL传递给绑定的浏览器。 Example 17-4 Loading Remote Web Pages 复制代码 1. import javafx.application.Application; 2. import javafx.event.ActionEvent; 3. import javafx.event.EventHandler; 4. import javafx.scene.*; 5. import javafx.scene.control.*; 6. import javafx.scene.image.Image; 7. import javafx.scene.image.ImageView; 8. import javafx.scene.layout.HBox; 9. import javafx.scene.layout.Priority; 10. import javafx.scene.layout.VBox; 11. import javafx.scene.text.Font; 12. import javafx.scene.web.WebEngine; 13. import javafx.scene.web.WebView; 14. import javafx.stage.Stage; 15. 16. public class Main extends Application { 17. 18.     final static String[] imageFiles = new String[]{ 19.         "product.png", 20.         "education.png", 21.         "partners.png", 22.         "support.png" 23.     }; 24.     final static String[] captions = new String[]{ 25.         "Products", 26.         "Education", 27.         "Partners", 28.         "Support" 29.     }; 30. 31.     final static String[] urls = new String[]{ 32.         "http://www.oracle.com/us/products/index.html", 33.         "http://education.oracle.com/", 34.         "http://www.oracle.com/partners/index.html", 35.         "http://www.oracle.com/us/support/index.html" 36.     }; 37.      38.     final ImageView selectedImage = new ImageView(); 39.     final Hyperlink[] hpls = new Hyperlink[captions.length]; 40.     final Image[] images = new Image[imageFiles.length];   41. 42.     public static void main(String[] args){ 43.         launch(args); 44.     } 45. 46.     @Override 47.     public void start(Stage stage) { 48.         VBox vbox = new VBox(); 49.         Scene scene = new Scene(vbox); 50.         stage.setTitle("Hyperlink Sample"); 51.         stage.setWidth(570); 52.         stage.setHeight(550); 53. 54.         selectedImage.setLayoutX(100); 55.         selectedImage.setLayoutY(10); 56.          57.         final WebView browser = new WebView(); 58.         final WebEngine webEngine = browser.getEngine(); 59. 60.         for (int i = 0; i < captions.length; i++) { 61.             final Hyperlink hpl = hpls[i] = new Hyperlink(captions[i]); 62. 63.             final Image image = images[i] = 64.                     new Image(getClass().getResourceAsStream(imageFiles[i])); 65.             hpl.setGraphic(new ImageView (image)); 66.             hpl.setFont(Font.font("Arial", 14)); 67.             final String url = urls[i]; 68. 69.             hpl.setOnAction(new EventHandler() { 70.                 @Override 71.                 public void handle(ActionEvent e) { 72.                     webEngine.load(url); 73.                 } 74.             }); 75.         } 76.                77.         HBox hbox = new HBox(); 78.         hbox.getChildren().addAll(hpls); 79. 80.         vbox.getChildren().addAll(hbox, browser); 81.         VBox.setVgrow(browser, Priority.ALWAYS); 82.          83.         stage.setScene(scene); 84.         stage.show(); 85.     } 86. } 超链接也在for循环中创建,类似于 Example 17-2 。为超链接设置的行为从urls数组到 WebEngine 对象传递了相应的URL。 编译运行效果如Figure 17-4 . Figure 17-4 Loading Pages from the Oracle Corporate Site 第二十四回 JavaFX2.0 提示条ToolTip Tooltip类产生一个常见的UI控件,一般用来为UI控件添加信息。把鼠标放在控件上提示条就显示出来。任何控件使用 setTooltip方法都能添加提示条。 提示条有2个状态:激活的和显示的。当鼠标放置在控件上时提示条激活,当它显示出来就是“显示的”状态,显示的提示条也是激活的。在提示条激活和显示之间有一些延迟。 带有提示条的密码框见Figure 18-1 . Figure 18-1 Tooltip Added to a Password Field Description of "Figure 18-1 Tooltip Added to a Password Field" 创建Tooltip 研究 Example 18-1 中的代码,它创建的是上面的应用。 复制代码 1. Example 18-1 Adding a Tooltip to the Password Field 2. final PasswordField pf = new PasswordField(); 3. final Tooltip tooltip = new Tooltip(); 4. tooltip.setText( 5.     "\nYour password must be\n" + 6.     "at least 8 characters in length\n"  + 7. ); 8. pf.setTooltip(tooltip); javafx.scene.control包中的每个控件都具有添加提示条的 setTooltip 方法。可以定义文本,使用Tooltip的构造方法或 setText 方法 。 由于 Tooltip 类继承了Labeled 类,所以不仅可以添加文本,也可以添加图形。 Example 18-2 中的代码块为密码框的提示条添加了图标。 Example 18-2 Adding an Icon to a Tooltip Image image = new Image(     getClass().getResourceAsStream("warn.png") ); tooltip.setGraphic(new ImageView(image)); 运行效果见 Figure 18-2 Figure 18-2 Tooltip with an Icon Description of "Figure 18-2 Tooltip with an Icon" 提示条不仅能提供辅助信息,也能呈现数据。 在提示条中呈现数据 Figure 18-3 中的应用使用显示在提示条中的信息来计算酒店住宿的总费用。 Figure 18-3 Calculating Hotel Rates Description of "Figure 18-3 Calculating Hotel Rates" Each checkbox is accompanied by a tooltip.每个复选框有一个提示条,每个提示条显示一个特定预定项目的费用。如果用户选择了复选框,相应的值就加到总数中。当然取消选中后也会从总数中减去。 看下该应用的代码Example 18-3 . Example 18-3 Using Tooltips to Calculate Hotel Rates 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.geometry.Insets; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.CheckBox; 8. import javafx.scene.control.Label; 9. import javafx.scene.control.Tooltip; 10. import javafx.scene.layout.HBox; 11. import javafx.scene.layout.VBox; 12. import javafx.scene.text.Font; 13. import javafx.stage.Stage; 14. 15. 16. public class Main extends Application { 17. 18.     final static String[] rooms = new String[]{ 19.         "Accommodation (BB)", 20.         "Half Board", 21.         "Late Check-out", 22.         "Extra Bed" 23.     }; 24.     final static Integer[] rates = new Integer[]{ 25.         100, 20, 10, 30 26.     }; 27.     final CheckBox[] cbs = new CheckBox[rooms.length]; 28.     final Label total = new Label("Total: $0"); 29.     Integer sum = 0; 30. 31.     public static void main(String[] args) { 32.         launch(args); 33.     } 34. 35.     @Override 36.     public void start(Stage stage) { 37.         Scene scene = new Scene(new Group()); 38.         stage.setTitle("Tooltip Sample"); 39.         stage.setWidth(300); 40.         stage.setHeight(150); 41. 42.         total.setFont(new Font("Arial", 20)); 43.          44.         for (int i = 0; i < rooms.length; i++) { 45.             final CheckBox cb = cbs[i] = new CheckBox(rooms[i]); 46.             final Integer rate = rates[i]; 47.             final Tooltip tooltip = new Tooltip("$" + rates[i].toString()); 48.             tooltip.setFont(new Font("Arial", 16)); 49.             cb.setTooltip(tooltip); 50.             cb.selectedProperty().addListener(new ChangeListener() { 51.                 public void changed(ObservableValue ov, 52.                     Boolean old_val, Boolean new_val) { 53.                     if (cb.isSelected()) { 54.                         sum = sum + rate; 55.                     } else { 56.                         sum = sum - rate; 57.                     } 58.                     total.setText("Total: $" + sum.toString()); 59.                 } 60.             }); 61.         } 62. 63.         VBox vbox = new VBox(); 64.         vbox.getChildren().addAll(cbs); 65.         vbox.setSpacing(5); 66.         HBox root = new HBox(); 67.         root.getChildren().add(vbox); 68.         root.getChildren().add(total); 69.         root.setSpacing(40); 70.         root.setPadding(new Insets(20, 10, 10, 20)); 71. 72.         ((Group) scene.getRoot()).getChildren().add(root); 73. 74.         stage.setScene(scene); 75.         stage.show(); 76.     } 77. } Example 18-4 中的代码加入到 Example 18-3 中来创建一个提示条并分配了一个文本。项目价格的Integer 值被转化成了String 值。 Example 18-4 Setting the Value for a Tooltip final Tooltip tooltip = new Tooltip("$" + rates.toString()) 可以通过使用CSS来改变其外观。 第二十五回 JavaFX2.0 Html编辑器 HTMLEditor控件是一个全功能的富文本编辑器。除了基本编辑功能外,它还支持以下特性: 文本格式,包括粗体、斜体、下划线等等 段落设置,比如格式、字体、字号 前景和背景颜色 文本缩进 圆点和数字列表 文本对齐 添加水平标尺 复制和粘贴文本块 Figure 19-1 是一个JavaFX应用中的富文本编辑器。 Figure 19-1 HTML Editor Description of "Figure 19-1 HTML Editor" HTMLEditor 类呈现编辑内容使用的是HTML字符串形式,比如说,Figure 19-1 中的内容呈现以下字符串:"

Heading

Text, some text
." 由于HTMLEditor 类继承了Node 类,所以可以为它的实例应用视效和转换。 添加HTML Editor 和其他UI控件一样, HTMLEditor组件必须加入场景才能在应用中显示。可以像 Example 19-1 这样直接添加,或者通过布局容器。 Example 19-1 Adding an HTML Editor to a JavaFX Application 复制代码 1. import javafx.application.Application; 2. import javafx.scene.Scene; 3. import javafx.scene.web.HTMLEditor; 4. import javafx.stage.Stage; 5. 6. public class HTMLEditorSample extends Application { 7. 8.     @Override 9.     public void start(Stage stage) { 10.         stage.setTitle("HTMLEditor Sample"); 11.         stage.setWidth(400); 12.         stage.setHeight(300);   13.         final HTMLEditor htmlEditor = new HTMLEditor(); 14.         htmlEditor.setPrefHeight(245); 15.         Scene scene = new Scene(htmlEditor);       16.         stage.setScene(scene); 17.         stage.show(); 18.     } 19. 20.     public static void main(String[] args) { 21.         launch(args); 22.     } 23. } 编译运行上面的代码效果是 Figure 19-2 . Figure 19-2 Initial View of the HTMLEditor Component Description of "Figure 19-2 Initial View of the HTMLEditor Component" 实现该组件后就有格式栏,不能关闭它们。不过也可以使用CSS来改变其外观。见Example 19-2 . Example 19-2 Styling the HTMLEditor htmlEditor.setStyle(     "-fx-font: 12 cambria;"     + "-fx-border-color: brown; "     + "-fx-border-style: dotted;"     + "-fx-border-width: 2;" ); 把它们加入到 Example 19-1 ,效果是Figure 19-3 . Figure 19-3 Alternative View of the HTMLEditor Component Description of "Figure 19-3 Alternative View of the HTMLEditor Component" 现在组件的边框和格式栏的字体改变了。 HTMLEditor 类提供了一个方法来定义应用启动后编辑区显示的内容。使用setHtmlText 方法来设置编辑器的初始文本,见Example 19-3。 Example 19-3 Setting the Text Content private final String INITIAL_TEXT = "Lorem ipsum dolor sit "     + "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar "     + "in scelerisque cursus, pulvinar at ante. Nulla consequat"     + "congue lectus in sodales. Nullam eu est a felis ornare "     + "bibendum et nec tellus. Vivamus non metus tempus augue auctor "     + "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. "     + "Integer congue faucibus dapibus. Integer id nisl ut elit "     + "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum "     + "sem."; htmlEditor.setHtmlText(INITIAL_TEXT); Figure 19-4 是使用setHTMLText 方法后的编辑器。 Figure 19-4 HTMLEditor with the Predefined Text Content Description of "Figure 19-4 HTMLEditor with the Predefined Text Content" 可以在字符串中使用HTML标签来指定初始显示的文本格式。 用HTML Editor构建用户接口 能够使用 HTMLEditor 控件来实现典型的用户接口,比如说可以实现消息服务、email客户端、甚至内容管理系统。 下面实现一个消息排版窗口,在很多email客户端应用中都可以找到它。 Example 19-4 HTMLEditor Added to the Email Client UI 复制代码 1. import javafx.application.Application; 2. import javafx.collections.FXCollections; 3. import javafx.geometry.Insets; 4. import javafx.geometry.Pos; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.*; 8. import javafx.scene.layout.GridPane; 9. import javafx.scene.layout.VBox; 10. import javafx.scene.web.HTMLEditor; 11. import javafx.stage.Stage; 12. 13. public class HTMLEditorSample extends Application { 14.      15.     @Override 16.     public void start(Stage stage) { 17.         stage.setTitle("Message Composing"); 18.         stage.setWidth(500); 19.         stage.setHeight(500); 20.         Scene scene = new Scene(new Group()); 21.      22.         final VBox root = new VBox();         23.         root.setPadding(new Insets(8, 8, 8, 8)); 24.         root.setSpacing(5); 25.         root.setAlignment(Pos.BOTTOM_LEFT); 26.          27.         final GridPane grid = new GridPane(); 28.         grid.setVgap(5); 29.         grid.setHgap(10); 30.                31.         final ChoiceBox sendTo = 32.             new ChoiceBox(FXCollections.observableArrayList( 33.                 "To:", "Cc:", "Bcc:") 34.         ); 35.          36.         sendTo.setPrefWidth(100);                 37.         GridPane.setConstraints(sendTo, 0, 0); 38.         grid.getChildren().add(sendTo); 39.          40.         final TextField tbTo = new TextField(); 41.         tbTo.setPrefWidth(400); 42.         GridPane.setConstraints(tbTo, 1, 0); 43.         grid.getChildren().add(tbTo); 44.          45.         final Label subjectLabel = new Label("Subject:"); 46.         GridPane.setConstraints(subjectLabel, 0, 1); 47.         grid.getChildren().add(subjectLabel);         48.          49.         final TextField tbSubject = new TextField(); 50.         tbTo.setPrefWidth(400); 51.         GridPane.setConstraints(tbSubject, 1, 1); 52.         grid.getChildren().add(tbSubject); 53.          54.         root.getChildren().add(grid); 55.          56.         final HTMLEditor htmlEditor = new HTMLEditor(); 57.         htmlEditor.setPrefHeight(370); 58. 59.         root.getChildren().addAll(htmlEditor, new Button("Send"));         60.        61.         final Label htmlLabel = new Label(); 62.         htmlLabel.setWrapText(true); 63.                        64.         scene.setRoot(root); 65.         stage.setScene(scene); 66.         stage.show(); 67.     } 68. 69.     public static void main(String[] args) { 70.         launch(args); 71.     } 72. } 该接口包括一个选项框来选择接受类型,2个文本框来输入email地址和主题,一个标签来显示主题字段,一个编辑器,还有发送按钮。 使用Grid 和VBox布局容器把这些UI控件加入到应用的场景中。编译运行的效果见 Figure 19-5 ,这是一个用户正在排版周报。 Figure 19-5 Email Client User Interface Description of "Figure 19-5 Email Client User Interface" 调 用setPrefWidth 或 setPrefHeight方法为 HTMLEditor对象设置宽或高 ,当然根本不指定也行。Example 19-4 中为组件高度指定了值,而宽度由VBox悲剧容器控制了。当内容文本超出了编辑区的宽度和高度时,垂直滚动条就显示出来。 获取HTML内容 用JavaFX HTMLEditor控件,你可以编辑和设置初始内容。此外,你还可以以HTML格式获得输入的和编辑的内容。具体实现见 Example 19-5 Example 19-5 Retrieving HTML Code 复制代码 1. import javafx.application.Application; 2. import javafx.event.ActionEvent; 3. import javafx.event.EventHandler; 4. import javafx.geometry.Insets; 5. import javafx.geometry.Pos; 6. import javafx.scene.Group; 7. import javafx.scene.Scene; 8. import javafx.scene.control.*; 9. import javafx.scene.layout.VBox; 10. import javafx.scene.web.HTMLEditor; 11. import javafx.stage.Stage; 12. 13. public class HTMLEditorSample extends Application {     14.     private final String INITIAL_TEXT = "Lorem ipsum dolor sit " 15.             + "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar " 16.             + "in scelerisque cursus, pulvinar at ante. Nulla consequat" 17.             + "congue lectus in sodales. Nullam eu est a felis ornare " 18.             + "bibendum et nec tellus. Vivamus non metus tempus augue auctor " 19.             + "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. " 20.             + "Integer congue faucibus dapibus. Integer id nisl ut elit " 21.             + "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum " 22.             + "sem."; 23. 24.     @Override 25.     public void start(Stage stage) { 26.         stage.setTitle("HTMLEditor Sample"); 27.         stage.setWidth(500); 28.         stage.setHeight(500); 29.         Scene scene = new Scene(new Group()); 30.      31.         VBox root = new VBox();       32.         root.setPadding(new Insets(8, 8, 8, 8)); 33.         root.setSpacing(5); 34.         root.setAlignment(Pos.BOTTOM_LEFT); 35.                36.         final HTMLEditor htmlEditor = new HTMLEditor(); 37.         htmlEditor.setPrefHeight(245); 38.         htmlEditor.setHtmlText(INITIAL_TEXT);       39. 40.         final TextArea htmlCode = new TextArea(); 41.         htmlCode.setWrapText(true); 42.      43.         ScrollPane scrollPane = new ScrollPane(); 44.         scrollPane.getStyleClass().add("noborder-scroll-pane"); 45.         scrollPane.setContent(htmlCode); 46.         scrollPane.setFitToWidth(true); 47.         scrollPane.setPrefHeight(180); 48. 49.         Button showHTMLButton = new Button("Produce HTML Code"); 50.         root.setAlignment(Pos.CENTER); 51.         showHTMLButton.setOnAction(new EventHandler() { 52.             @Override public void handle(ActionEvent arg0) { 53.                 htmlCode.setText(htmlEditor.getHtmlText()); 54. 55. 56. 57. 58.             } 59.         }); 60.          61.         root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane); 62.         scene.setRoot(root); 63. 64.         stage.setScene(scene); 65.         stage.show(); 66.     } 67. 68.     public static void main(String[] args) { 69.         launch(args); 70.     } 71. } getHTMLText方法获得了编辑内容并以HTML字符串形式呈现。该内容传递给文本区,这样就能查看、复制、粘贴这些HTML代码。 Figure 19-6 就是样例。 Figure 19-6 Obtaining the HTML Content Description of "Figure 19-6 Obtaining the HTML Content" 类似地,也可以获得HTML代码比in个保存为文件或者发送到WebView对象,以同步编辑器和绑定的浏览器中的内容。 下面实现了这个任务 Example 19-6 . Example 19-6 Rendering Edited HTML Content in a Browser 复制代码 1. import javafx.application.Application; 2. import javafx.event.ActionEvent; 3. import javafx.event.EventHandler; 4. import javafx.geometry.Insets; 5. import javafx.geometry.Pos; 6. import javafx.scene.Group; 7. import javafx.scene.Scene; 8. import javafx.scene.control.*; 9. import javafx.scene.layout.VBox; 10. import javafx.scene.web.HTMLEditor; 11. import javafx.scene.web.WebEngine; 12. 13. 14. 15. 16. import javafx.scene.web.WebView; 17. 18. 19. 20. 21. import javafx.stage.Stage; 22. 23. public class HTMLEditorSample extends Application { 24.     private final String INITIAL_TEXT = "Lorem ipsum dolor sit " 25.             + "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar " 26.             + "in scelerisque cursus, pulvinar at ante. Nulla consequat" 27.             + "congue lectus in sodales. Nullam eu est a felis ornare " 28.             + "bibendum et nec tellus. Vivamus non metus tempus augue auctor " 29.             + "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. " 30.             + "Integer congue faucibus dapibus. Integer id nisl ut elit " 31.             + "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum " 32.             + "sem."; 33. 34.     @Override 35.     public void start(Stage stage) { 36.         stage.setTitle("HTMLEditor Sample"); 37.         stage.setWidth(500); 38.         stage.setHeight(500); 39.         Scene scene = new Scene(new Group()); 40.      41.         VBox root = new VBox();     42.         root.setPadding(new Insets(8, 8, 8, 8)); 43.         root.setSpacing(5); 44.         root.setAlignment(Pos.BOTTOM_LEFT); 45. 46.         final HTMLEditor htmlEditor = new HTMLEditor(); 47.         htmlEditor.setPrefHeight(245); 48.         htmlEditor.setHtmlText(INITIAL_TEXT); 49.          50.         final WebView browser = new WebView(); 51. 52. 53. 54. 55.         final WebEngine webEngine = browser.getEngine(); 56. 57. 58. 59. 60.      61.         ScrollPane scrollPane = new ScrollPane(); 62.         scrollPane.getStyleClass().add("noborder-scroll-pane"); 63.         scrollPane.setStyle("-fx-background-color: white"); 64.         scrollPane.setContent(browser); 65.         scrollPane.setFitToWidth(true); 66.         scrollPane.setPrefHeight(180); 67. 68.         Button showHTMLButton = new Button("Load Content in Browser"); 69.         root.setAlignment(Pos.CENTER); 70.         showHTMLButton.setOnAction(new EventHandler() { 71.             @Override public void handle(ActionEvent arg0) {                 72.                 webEngine.loadContent(htmlEditor.getHtmlText()); 73. 74. 75. 76. 77.             } 78.         }); 79.          80.         root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane); 81.         scene.setRoot(root); 82. 83.         stage.setScene(scene); 84.         stage.show(); 85.     } 86. 87.     public static void main(String[] args) { 88.         launch(args); 89.     } 90. } 从htmlEditor 组件获得HTML代码加载到WebEngine 对象来指定绑定浏览器的内容。每次用户点击Load Content in Browser按钮,编辑的文本就更新到浏览器中。Figure 19-7 是Example 19-6 运行的效果。 Figure 19-7 Loading Content in a Browser Description of "Figure 19-7 Loading Content in a Browser" 使用Text 组件来添加非编辑文本内容。到Using Text and Text Effects in JavaFX 了解更多Text组件。 第二十六回 JavaFX2.0 标题窗格TitledPane和手风琴控件Accordion 标题窗格就是带有标题的面板,可以被打开和关闭,也可以被包进任何Node元素,诸如UI控件、图片、计入布局容器的元素组。 标题窗格可以用手风琴控件来形成组。手风琴控件能创建多个窗格而每次只显示一个。Figure 20-1 是带有3个标题窗格的手风琴控件。 Figure 20-1 Accordion with Three Titled Panes Description of "Figure 20-1 Accordion with Three Titled Panes" JavaFX SDK API中的 Accordion 和 TitledPane 类用来实现这样的控件。 创建Titled Panes 创建TitledPane 控件要定义一个标题和一些内容。可以使用TitledPane 类的带有两个参数的构造方法,或者单独使用setText 和setContent 方法也行。两种方法都在Example 20-1 中 . Example 20-1 Declaring a TitledPane Object //using a two-parameter constructor TitledPane tp = new TitledPane("My Titled Pane", new Button("Button")); //applying methods TitledPane tp = new TitledPane(); tp.setText("My Titled Pane"); tp.setContent(new Button("Button")); 它们的效果系统,都是 Figure 20-2 . Figure 20-2 Titled Pane with a Button Description of "Figure 20-2 Titled Pane with a Button" 标题窗格可以改变大小来适应它的内容。可以添加多行文本,结果见Figure 20-3 . Figure 20-3 Titled Pane with Some Text Description of "Figure 20-3 Titled Pane with Some Text" 不要明确指定标题窗格的最小、最大和优先的高度值,这样在打开关闭时可能导致难以预料的行为。 Example 20-2 在的代码添加了几个控件到标题窗格,然后加入到了GridPane 布局容器。 Example 20-2 Titled Pane with the GridPane Layout Container 复制代码 1. TitledPane gridTitlePane = new TitledPane(); 2. 3. 4. GridPane grid = new GridPane(); 5. grid.setVgap(4); 6. grid.setPadding(new Insets(5, 5, 5, 5)); 7. grid.add(new Label("First Name: "), 0, 0); 8. grid.add(new TextField(), 1, 0); 9. grid.add(new Label("Last Name: "), 0, 1); 10. grid.add(new TextField(), 1, 1); 11. grid.add(new Label("Email: "), 0, 2); 12. grid.add(new TextField(), 1, 2);         13. gridTitlePane.setText("Grid"); 14. 15. 16. gridTitlePane.setContent(grid); 运行的结果是 Figure 20-4 。 Figure 20-4 Titled Pane that Contains Several Controls Description of "Figure 20-4 Titled Pane that Contains Several Controls" 可 以定义标题窗格打开关闭的方式。默认地,标题窗格是可伸缩的,它们的移动也是动画。如果要阻止标题窗格关闭,将setCollapsible方法 设为false。 也可以将 setAnimated 方法设为false来关闭动画打开效果。 Example 20-3 中的代码实现了该任务。 Example 20-3 Adjusting the Style of a Titled Pane TitledPane tp = new TitledPane(); //prohibit closing tp.setCollapsible(false); //prohibit animating tp.setAnimated(false); 将Titled Panes加入到Accordion 在应用中,可以单独使用标题窗格,也可以使用Accordion 把控件编组。同样也不要指定手风琴控件的高度值。 将几个标题窗格加入到手风琴很类似把开关按钮加入到开关组:每次只能打开手风琴中的一个标题窗格。Example 20-4 创建了3个标题窗格并加入到了手风琴中。 Example 20-4 Accordion and Three Titled Panes 复制代码 1. import javafx.application.Application; 2. import javafx.scene.Group; 3. import javafx.scene.Scene; 4. import javafx.scene.control.Accordion; 5. import javafx.scene.control.TitledPane; 6. import javafx.scene.image.Image; 7. import javafx.scene.image.ImageView; 8. import javafx.scene.paint.Color; 9. import javafx.stage.Stage; 10. 11. public class TitledPaneSample extends Application { 12.     final String[] imageNames = new String[]{"Apples", "Flowers", "Leaves"}; 13.     final Image[] images = new Image[imageNames.length]; 14.     final ImageView[] pics = new ImageView[imageNames.length]; 15.     final TitledPane[] tps = new TitledPane[imageNames.length]; 16.            17.     public static void main(String[] args) { 18.         launch(args); 19.     } 20. 21.     @Override public void start(Stage stage) { 22.         stage.setTitle("TitledPane"); 23.         Scene scene = new Scene(new Group(), 80, 180); 24.         scene.setFill(Color.GHOSTWHITE); 25.                                26.         final Accordion accordion = new Accordion (); 27. 28.    29.          30.         for (int i = 0; i < imageNames.length; i++) { 31. 32.            33.             images[i] = new 34.                 Image(getClass().getResourceAsStream(imageNames[i] + ".jpg")); 35. 36.             pics[i] = new ImageView(images[i]); 37. 38.             tps[i] = new TitledPane(imageNames[i],pics[i]); 39. 40.         }   41.         accordion.getPanes().addAll(tps); 42. 43.         accordion.setExpandedPane(tps[0]); 44. 45.         Group root = (Group)scene.getRoot(); 46.         root.getChildren().add(accordion); 47.         stage.setScene(scene); 48.         stage.show(); 49.     } 50. } 用循环创建了3个标题窗格,每个的内容都是ImageView 对象。把标题窗格加入到手风琴中要使用getPanes 和addAll 方法。可以用add 方法代替addAll 方法来加入单个标题窗格。 默认地,应用启动后所有窗格都关着。setExpandedPane方法指定了带有苹果图片的窗格要打开。见 Figure 20-5 . Figure 20-5 Accordion with Three Titled Panes Description of "Figure 20-5 Accordion with Three Titled Panes" 处理Accordion事件 可以使用标题窗格和手风琴程序不同的数据。Example 20-5 创建了一个单独的标题窗格放进GridPane 悲剧容器和三个标题窗格放进手风琴中。单独的窗格包含了一个email客户端元素,手风琴使得选择窗格会显示相应的图片。 Example 20-5 Implementing ChangeListener for an Accordion 复制代码 1. import javafx.application.Application; 2. import javafx.beans.value.ChangeListener; 3. import javafx.beans.value.ObservableValue; 4. import javafx.geometry.Insets; 5. import javafx.scene.Group; 6. import javafx.scene.Scene; 7. import javafx.scene.control.Accordion; 8. import javafx.scene.control.Label; 9. import javafx.scene.control.TextField; 10. import javafx.scene.control.TitledPane; 11. import javafx.scene.image.Image; 12. import javafx.scene.image.ImageView; 13. import javafx.scene.layout.GridPane; 14. import javafx.scene.layout.HBox; 15. import javafx.scene.paint.Color; 16. import javafx.stage.Stage; 17. 18. public class TitledPaneSample extends Application { 19.     final String[] imageNames = new String[]{"Apples", "Flowers", "Leaves"}; 20.     final Image[] images = new Image[imageNames.length]; 21.     final ImageView[] pics = new ImageView[imageNames.length]; 22.     final TitledPane[] tps = new TitledPane[imageNames.length]; 23.     final Label label = new Label("N/A"); 24.        25.     public static void main(String[] args) { 26.         launch(args); 27.     } 28. 29.     @Override public void start(Stage stage) { 30.         stage.setTitle("TitledPane"); 31.         Scene scene = new Scene(new Group(), 800, 250); 32.         scene.setFill(Color.GHOSTWHITE); 33.          34.         // --- GridPane container 35.         TitledPane gridTitlePane = new TitledPane(); 36.         GridPane grid = new GridPane(); 37.         grid.setVgap(4); 38.         grid.setPadding(new Insets(5, 5, 5, 5)); 39.         grid.add(new Label("To: "), 0, 0); 40.         grid.add(new TextField(), 1, 0); 41.         grid.add(new Label("Cc: "), 0, 1); 42.         grid.add(new TextField(), 1, 1); 43.         grid.add(new Label("Subject: "), 0, 2); 44.         grid.add(new TextField(), 1, 2);         45.         grid.add(new Label("Attachment: "), 0, 3); 46.         grid.add(label,1, 3); 47.         gridTitlePane.setText("Grid"); 48.         gridTitlePane.setContent(grid); 49.          50.         // --- Accordion 51.         final Accordion accordion = new Accordion ();                 52.         for (int i = 0; i < imageNames.length; i++) { 53.             images[i] = new 54.                 Image(getClass().getResourceAsStream(imageNames[i] + ".jpg")); 55.             pics[i] = new ImageView(images[i]); 56.             tps[i] = new TitledPane(imageNames[i],pics[i]); 57.         }   58.         accordion.getPanes().addAll(tps);         59.         accordion.expandedPaneProperty().addListener(new 60. 61.             ChangeListener() { 62. 63.                 public void changed(ObservableValue ov, 64. 65.                     TitledPane old_val, TitledPane new_val) { 66. 67.                         if (new_val != null) { 68. 69.                             label.setText(accordion.getExpandedPane().getText() + 70. 71.                                 ".jpg"); 72. 73.                         } 74. 75.              } 76.         }); 77. 78.          79.         HBox hbox = new HBox(10); 80.         hbox.setPadding(new Insets(20, 0, 0, 20)); 81.         hbox.getChildren().setAll(gridTitlePane, accordion); 82. 83.         Group root = (Group)scene.getRoot(); 84.         root.getChildren().add(hbox); 85.         stage.setScene(scene); 86.         stage.show(); 87.     } 88. } 当打开手风琴中的标题窗格时,手风琴的 expandedPaneProperty 属性就会改变。ChangeListener对象通报了该变化,而手风琴中打开的标题窗格就构建一个文件名,该文件名就作为相应 Label对象的文本。 Figure 20-6 是应用启动后的样子,Attachment标签是"N/A,"因为没有窗格被选中。 Figure 20-6 Initial View of the TitledPaneSample Application Description of "Figure 20-6 Initial View of the TitledPaneSample Application" 如果打开的是Leaves标题窗格,Attachment标签就变成"Leaves.jpg,"见Figure 20-7 . Figure 20-7 TitledPaneSample Application with the Leaves Titled Pane Expanded Description of "Figure 20-7 TitledPaneSample Application with the Leaves Titled Pane Expanded" TitledPane 和Accordion 类都继承了Node类,所以可以应用特效和使用CSS。 第二十七回 JavaFX2.0 文本Text及其特效 文本讲述如何在JavaFX2.0应用中加入文本和如何为文本提供花俏的效果。 引子 JavaFX 2.0应用的图形内容包含一些对象,它们被组织在一个成为场景图的类树结构中。场景图中的每个元素成为一个结点,结点可以管理很多不同种类的内容,包括文 本。结点可以转换和移动,也可以应用多种效果。为所有结点类型使用共同特点使得可以提供复杂的文本内容来满足现在的富网络应用(RIAs). JavaFX 2.0发布版提供了javafx.scene.text.Text类用来显示文本。 Text类继承自 Node 类,所以可以为其应用特效、动画、转换,和其他结点一样的。而又Node类继承自 Shape 类,可以像其他形状一样为其设置描边和填充效果。 添加Text To add a text object to your application, 要添加文本,使用下面任一构造方法。Example 1 到Example 3 . Example 1 Text t = new Text(); t.setText("This is a text sample"); Example 2 Text t = new Text("This is a text sample"); Example 3 Text t = new Text (10, 20, "This is a text sample"); 也可以使用javafx.scene.text.TextBuilder 类创建,见Example 4 . Example 4 Text t = TextBuilder.create().text("This is a text sample").build(); 设置字体和颜色 添加文本后就可以设置一些属性了。要设置使用的字体,实例化javafx.scene.text.Font 类。Font.font()方法可以指定字体和字号,也可以像下面这样设置颜色 Example 5 . Example 5 t.setText("This is a text sample"); t.setFont(Font.font ("Verdana", 20)); t.setFill(Color.RED); 或者使用系统字体,它是依赖于不同OS平台的。这样的话,使用Font.getDefault() 方法。 不使用单一颜色,也可以使用线性渐变填充,见Example 6 . Example 6 Text text = TextBuilder.create().text("Stroke and Fill").                     font(Font.font("Tahoma", 100)).build(); text.setFill(new LinearGradient(0, 0, 1, 2, true, CycleMethod.REPEAT, new          Stop[]{new Stop(0, Color.AQUA), new Stop(0.5f, Color.RED)})); text.setStrokeWidth(1); text.setStroke(Color.BLACK); 效果如下 Figure 1 . Figure 1 Text with a Linear Gradient Filling Description of "Figure 1 Text with a Linear Gradient Filling" 设置粗体和斜体 font 方法的 FontWeight 常数可以设置粗体,见Example 7 . Example 7 t.setFont(Font.font("Verdana", FontWeight.BOLD, 70)); FontPosture 常数来设置斜体,见Example 8 . Example 8 t.setFont(Font.font("Verdana", FontPosture.ITALIC, 20)); 使用定制Font 如果需要使用某种独特的字体而其他机器上很可能没有安装,可以在 JavaFX 2.0应用中包含TrueType字体 (.ttf) 或OpenType字体 (.otf)。 要包含 TrueType或 OpenType字体,这样做: 在工程目录下创建 resources/fonts文件夹 把字体文件复制到fonts子文件夹下 在代码中找下面这样加载字体。Example 9 . Example 9 text.setFont(Font.loadFont("file:resources/fonts/isadoracyr.ttf", 120)); 该字体如下 Figure 2 . Figure 2 Custom Font Description of "Figure 2 Custom Font" 应用Effect JavaFX 2.0发布版在javafx.scene.effect 包中提供了大量特效。前面提到,可以为文本结点应用特效。完整的效果集合,查看API文档。可以通过TextEffects示例应用查看一些效果。该应用中文本结点使用了一系列效果。从http://download.oracle.com/javafx/2.0/text/TextEffects.java.html 下载 texteffects.zip 文件并解压到本地,用NB打开为工程。 透视效果 PerspectiveTransform 类使得可以在二维内容中模拟三维效果。透视转换可以将一个任意的四边形映射为另一个四边形。输入就是一个结点,而输出依赖于指定的四个角的X和Y坐标。 在TextEffects 应用中,PerspectiveTransform 效果是为一组包含矩形和文本的group应用的,见Example 10 。 Example 10 复制代码 1. PerspectiveTransform pt = new PerspectiveTransform(); 2. pt.setUlx(10.0f); 3. pt.setUly(10.0f); 4. pt.setUrx(310.0f); 5. pt.setUry(40.0f); 6. pt.setLrx(310.0f); 7. pt.setLry(60.0f); 8. pt.setLlx(10.0f); 9. pt.setLly(90.0f); 10. 11. g.setEffect(pt); 12. g.setCache(true); 13. Rectangle r = new Rectangle(); 14. r.setX(10.0f); 15. r.setY(10.0f); 16. r.setWidth(280.0f); 17. r.setHeight(80.0f); 18. r.setFill(Color.BLUE); 19. 20. Text t = new Text(); 21. t.setX(20.0f); 22. t.setY(65.0f); 23. t.setText("Perspective"); 24. t.setFill(Color.YELLOW); 25. t.setFont(Font.font(null, FontWeight.BOLD, 36)); 26. 27. g.getChildren().add(r); 28. g.getChildren().add(t); 29. return g; 效果如下 Figure 3 . Figure 3 Text with a Perspective Effect Description of "Figure 3 Text with a Perspective Effect" 模糊效果 GaussianBlur 类提供了基于高斯卷积内核的模糊效果。 Example 11 是一个应用了模糊效果的文本结点,见TextEffects 应用。 Example 11 复制代码 1. Text t2 = new Text(); 2. t2.setX(10.0f); 3. t2.setY(140.0f); 4. t2.setCache(true); 5. t2.setText("Blurry Text"); 6. t2.setFill(Color.RED); 7. t2.setFont(Font.font(null, FontWeight.BOLD, 36)); 8. t2.setEffect(new GaussianBlur()); 9. return t2; 效果如下 Figure 4 . Figure 4 Text with a Blur Effect Description of "Figure 4 Text with a Blur Effect " 外部阴影效果 要实现外部阴影效果,使用 DropShadow 类。可以为文本阴影指定一种颜色和偏移量。在TextEffects 应用中,文本是红色的,而外边阴影效果是3点的灰色。代码见Example 12 . Example 12 复制代码 1. DropShadow ds = new DropShadow(); 2. ds.setOffsetY(3.0f); 3. ds.setColor(Color.color(0.4f, 0.4f, 0.4f)); 4. 5. Text t = new Text(); 6. t.setEffect(ds); 7. t.setCache(true); 8. t.setX(10.0f); 9. t.setY(270.0f); 10. t.setFill(Color.RED); 11. t.setText("JavaFX drop shadow..."); 12. t.setFont(Font.font(null, FontWeight.BOLD, 32)); 效果如下 Figure 5 . Figure 5 Text with a Drop Shadow Effect Description of "Figure 5 Text with a Drop Shadow Effect" 内部阴影效果 内部阴影效果在内容的内边缘显示阴影。使用时也要指定颜色也偏移量。下面是在x和y方向都是4点偏移的内部阴影效果代码Example 13 . Example 13 复制代码 1. InnerShadow is = new InnerShadow(); 2. is.setOffsetX(4.0f); 3. is.setOffsetY(4.0f); 4. 5. Text t = new Text(); 6. t.setEffect(is); 7. t.setX(20); 8. t.setY(100); 9. t.setText("InnerShadow"); 10. t.setFill(Color.YELLOW); 11. t.setFont(Font.font(null, FontWeight.BOLD, 80)); 12. 13. t.setTranslateX(300); 14. t.setTranslateY(300); 15. 16. return t; Figure 6 Text with an Inner Shadow Effect Description of "Figure 6 Text with an Inner Shadow Effect" 反射 Reflection类呈现的是原始文本的倒影。也可以通过提供额外的参数来调整文本反射视图,如 底透明度、反射可见比、与原文距离、顶透明度。更多细节,查看API文档。 TextEffects 应用中的反射效果实现见Example 14 . Example 14 复制代码 1. Text t = new Text(); 2. t.setX(10.0f); 3. t.setY(50.0f); 4. t.setCache(true); 5. t.setText("Reflections on JavaFX..."); 6. t.setFill(Color.RED); 7. t.setFont(Font.font(null, FontWeight.BOLD, 30)); 8. 9. Reflection r = new Reflection(); 10. r.setFraction(0.7f); 11. 12. t.setEffect(r); 13. 14. t.setTranslateY(400); 15. return t; 运行后见 Figure 7 . Figure 7 Text with a Reflection Effect Description of "Figure 7 Text with a Reflection Effect" 整合多个效果 前面学了如何使用单一效果,要丰富文本内容就要组合多种效果,应用一个效果链来获得特殊的视觉感触。看下NeonSign 应用(点击下载) 的效果Figure 8 . Figure 8 Description of "Figure 8 " NeonSign 应用的图形场景中包含了以下元素: 背景使用的是一副砖墙图片 一个矩形提供了放射渐变填充 一个文本结点使用了效果链 一个文本域用于输入数据 该应用使用了一个绑定机制来设置文本结点显示输入的文本值。文本结点的文本属性(textProperty)绑定到了文本域的文本属性,见Example 15 . Example 15 Text text = new Text(); TextField textField = new TextField(); textField.setText("Neon Sign"); text.textProperty().bind(textField.textProperty()); 可以看到文本框输入的变化会引起文本结点的变化 文 本结点使用了效果链。主要的效果是一个混合效果,使用了MULTIPLY模式来结合两种输入:一个外部阴影效果和另一个混合效果 blend1。类似地, blend1 效果结合了一个外部阴影效果(ds1 )和一个混合效果 (blend2 )。blend2 效果结合了两种内部阴影效果。使用这个效果链和不同的颜色为文本对象应用了精细和复杂的颜色模式。下面是效果链的代码Example 16 . Example 16 复制代码 1. Blend blend = new Blend(); 2. blend.setMode(BlendMode.MULTIPLY); 3. 4. DropShadow ds = new DropShadow(); 5. ds.setColor(Color.rgb(254, 235, 66, 0.3)); 6. ds.setOffsetX(5); 7. ds.setOffsetY(5); 8. ds.setRadius(5); 9. ds.setSpread(0.2); 10. 11. blend.setBottomInput(ds); 12. 13. DropShadow ds1 = new DropShadow(); 14. ds1.setColor(Color.web("#f13a00")); 15. ds1.setRadius(20); 16. ds1.setSpread(0.2); 17. 18. Blend blend2 = new Blend(); 19. blend2.setMode(BlendMode.MULTIPLY); 20. 21. InnerShadow is = new InnerShadow(); 22. is.setColor(Color.web("#feeb42")); 23. is.setRadius(9); 24. is.setChoke(0.8); 25. blend2.setBottomInput(is); 26. 27. InnerShadow is1 = new InnerShadow(); 28. is.setColor(Color.web("#f13a00")); 29. is.setRadius(5); 30. is.setChoke(0.4); 31. blend2.setTopInput(is1); 32. 33. Blend blend1 = new Blend(); 34. blend1.setMode(BlendMode.MULTIPLY); 35. blend1.setBottomInput(ds1); 36. blend1.setTopInput(blend2); 37. 38. blend.setTopInput(blend1); 39. 40. text.setEffect(blend); 通过本文学习了如何添加文本和应用各种效果。更全面的信息,查看API文档。如果要在应用中实现一个文本编辑区,使用HTMLEditor组件。关于 HTMLEditor 控件的信息,查看第二十五回 。 第二十八回 使用JavaFX布局窗 JavaFX SDK提供了布局窗来支持不同的布局风格。本指南提供使用这些布局窗在JavaFX中创建图形化用户接口的信息。 假设读者具有中级的Java编程技术。 本指南包括以下主题: · Using Built-In Layout Panes - 讲述内置的布局窗并提供实例 · Tips for Sizing and Aligning Nodes - 提供覆盖结点默认大小和位置的例子 第二十九回 使用JavaFX2.0内置布局窗格 在JavaFX应用中,当然可以通过指定UI元素的位置和大小属性来手动布局。不过,更简单的方法是使用布局窗格。JavaFX SDK提高了多种布局容器类(称为窗格)来方便的建立和管理经典布局,如行、列、堆、拼贴等。由于窗口是可以改变大小的,所以布局窗格会根据其包含的结点 自动修改位置和大小。 本文是JavaFX布局窗格的概览,并为每个窗格提供了小例子。 边框窗格BorderPane BorderPane布局窗格提供了5块放置结点的区域:顶部、底部、座部、右部、中部。 Figure 1-1 是能用该布局窗格创建的布局类型。区域可以是任意大小的,如果不需要某一块,可以不定义。 Figure 1-1 Sample Border Pane Description of "Figure 1-1 Sample Border Pane" 边框窗格对于经典布局很有用,像顶部的工具栏,底部的状态栏,左边的导航面板,右边的补充信息,中间的工作区。 Example 1-1 创建了一个每个区域是有色矩形的边框窗格。 Example 1-1 Create a Border Pane 复制代码 1. BorderPane layout = new BorderPane(); 2. layout.setTop(new Rectangle(200, 50, Color.DARKCYAN)); 3. layout.setBottom(new Rectangle(200, 50, Color.DARKCYAN)); 4. layout.setCenter(new Rectangle(100, 100, Color.MEDIUMAQUAMARINE)); 5. layout.setLeft(new Rectangle(50, 100, Color.DARKTURQUOISE)); 6. layout.setRight(new Rectangle(50, 100, Color.DARKTURQUOISE)); 水平盒子HBox HBox 布局窗格提供了一种简单的方法来把一些列结点放进单行里面。Figure 1-2 是一个HBox 窗格的例子。 Figure 1-2 Sample HBox Pane Description of "Figure 1-2 Sample HBox Pane" Padding属性用来设置结点和HBox的边缘间距离。 Spacing属性用来设置结点间距离。style用来改变背景色。 Example 1-2 创建了一个工具栏的HBox 窗格,里面有两个按钮。 Example 1-2 Create an HBox Pane 复制代码 1. HBox hbox = new HBox(); 2. hbox.setPadding(new Insets(15, 12, 15, 12)); 3. hbox.setSpacing(10); 4. hbox.setStyle("-fx-background-color: #336699"); 5. 6. Button buttonCurrent = new Button("Current"); 7. buttonCurrent.setPrefSize(100, 20); 8. 9. Button buttonProjected = new Button("Projected"); 10. buttonProjected.setPrefSize(100, 20); 11. hbox.getChildren().addAll(buttonCurrent, buttonProjected); 12. 13. BorderPane border = new BorderPane(); 14. border.setTop(hbox); Example 1-2 中的最后一行创建了一个边框布局,并把HBox加入到顶部区域。结果见 Figure 1-3 . Figure 1-3 HBox Pane in a Border Pane Description of "Figure 1-3 HBox Pane in a Border Pane" 垂直盒子VBox VBox 布局窗格和HBox 布局很类似,区别仅仅是垂直盒子的结点是组织进一列中。Figure 1-4 是一个VBox窗格的例子。 Figure 1-4 Sample VBox Pane Description of "Figure 1-4 Sample VBox Pane" Padding属性用来设置结点和VBox的边缘间距离。 Spacing属性用来设置结点间距离。 Example 1-3 创建了一个选项列表VBox。 Example 1-3 Create a VBox Pane 复制代码 1. VBox vbox = new VBox(); 2. vbox.setPadding(new Insets(10, 10, 10, 10)); 3. vbox.setSpacing(10); 4. 5. Text title = new Text("Data"); 6. title.setFont(Font.font("Amble CN", FontWeight.BOLD, 14)); 7. vbox.getChildren().add(title); 8. 9. Text options[] = new Text[] { 10.                  new Text("  Sales"), 11.                  new Text("  Marketing"), 12.                  new Text("  Distribution"), 13.                  new Text("  Costs")}; 14. 15. for (int i=0; i<4; i++) { 16.      vbox.getChildren().add(options[i]); 17. } 18. 19. border.setLeft(vbox);       // Add to BorderPane from Example 1-2 Example 1-3 中最后一行把VBox窗格加入到了边框布局中的左部。结果见 Figure 1-5 . Figure 1-5 VBox Pane in a Border Pane Description of "Figure 1-5 VBox Pane in a Border Pane" 堆栈窗格StackPane StackPane布局窗格把所有结点放进一个堆栈中,新结点都在前一个结点上面。该布局模式可以方便地 在形状和图片上叠加文字或将多种简单形状组合成一个复杂形状。Figure 1-6 是一个帮助图标,是将一个问号放在了具有渐变背景的矩形上面。 Figure 1-6 Sample Stack Pane Description of "Figure 1-6 Sample Stack Pane" Example 1-4 为帮助图标创建了堆栈窗格。 Example 1-4 Create a Stack Pane 复制代码 1. StackPane stack = new StackPane(); 2. Rectangle helpIcon = new Rectangle(35.0, 25.0); 3. helpIcon.setFill(new LinearGradient(0,0,0,1, true, CycleMethod.NO_CYCLE, 4.     new Stop[]{ 5.     new Stop(0,Color.web("#4977A3")), 6.     new Stop(0.5, Color.web("#B0C6DA")), 7.     new Stop(1,Color.web("#9CB6CF")),})); 8. helpIcon.setStroke(Color.web("#D0E6FA")); 9. helpIcon.setArcHeight(3.5); 10. helpIcon.setArcWidth(3.5); 11. 12. Text helpText = new Text("?   "); 13. helpText.setFont(Font.font("Amble Cn", FontWeight.BOLD, 18)); 14. helpText.setFill(Color.WHITE); 15. helpText.setStroke(Color.web("#7080A0")); 16. 17. stack.getChildren().addAll(helpIcon, helpText); 18. stack.setAlignment(Pos.CENTER_RIGHT);     // Right-justify nodes in stack 19. 20. HBox.setHgrow(stack, Priority.ALWAYS);    // Give stack any extra space 21. hbox.getChildren().add(stack);            // Add to HBox from Example 1-2 Example 1-4 的最后一行把堆栈窗格加入到了HBox 中,并且让它永远在最右边。结果见Figure 1-7 . Figure 1-7 Stack Pane in an HBox Pane Description of "Figure 1-7 Stack Pane in an HBox Pane" 网格窗格GridPane GridPane 布局窗格可以灵活的创建放置结点的行和列的网络。结点可以放在任何网格细胞中,并且需要时还可以跨细胞。网格窗格用来创建表格或者是任何用行和列组织的布 局。Figure 1-8 是一个包含一个图标、小标题、文本和饼图的网格窗格。在图中,gridLinesVisible 属性用来设置显示网格线以看出行和列。该属性对于调试GridPane 布局很有用。 Figure 1-8 Sample Grid Pane Description of "Figure 1-8 Sample Grid Pane" Gap属性来控制行和列之间的空间。padding属性来控制结点和网格窗格边缘的距离。 Example 1-5 creates the grid pane shown in Figure 1-8 . Example 1-5 Create a Grid Pane 复制代码 1. GridPane grid = new GridPane(); 2. grid.setHgap(10); 3. grid.setVgap(10); 4. grid.setPadding(new Insets(0, 0, 0, 10)); 5. 6. // Category in column 2, row 1 7. Text category = new Text("Sales:"); 8. category.setFont(Font.font("Tahoma", FontWeight.BOLD, 20)); 9. grid.add(category, 1, 0); 10. 11. // Title in column 3, row 1 12. Text chartTitle = new Text("Current Year"); 13. chartTitle.setFont(Font.font("Tahoma", FontWeight.BOLD, 20)); 14. grid.add(chartTitle, 2, 0); 15. 16. // Subtitle in columns 2-3, row 2 17. Text chartSubtitle = new Text("Goods and Services"); 18. grid.add(chartSubtitle, 1, 1, 2, 1); 19. 20. // House icon in column 1, rows 1-2 21. ImageView imageHouse = new ImageView( 22.     new Image(LayoutSample.class.getResourceAsStream("graphics/house.png"))); 23. grid.add(imageHouse, 0, 0, 1, 2); 24. 25. // Left label in column 1 (bottom), row 3 26. Text goodsPercent = new Text("Goods\n80%"); 27. GridPane.setValignment(goodsPercent, VPos.BOTTOM); 28. grid.add(goodsPercent, 0, 2); 29. 30. // Chart in columns 2-3, row 3 31. ImageView imageChart = new ImageView( 32.     new Image(LayoutSample.class.getResourceAsStream("graphics/piechart.png"))); 33. grid.add(imageChart, 1, 2, 2, 1); 34. 35. // Right label in column 4 (top), row 3 36. Text servicesPercent = new Text("Services\n20%"); 37. GridPane.setValignment(servicesPercent, VPos.TOP); 38. grid.add(servicesPercent, 3, 2); 39. 40. border.setCenter(grid);       // Add to BorderPane from Example 1-2 Example 1-5 的最后一行把网格布局放到了边框布局的中间。结果见Figure 1-9 . Figure 1-9 Grid Pane in a Border Pane Description of "Figure 1-9 Grid Pane in a Border Pane" 由于窗口大小的变化,网格成功的结点会根据布局限制重置大小。 流窗格FlowPane FlowPane布局窗格中的结点会连续放置在窗格的边界集中。结点可以垂直流向 (columns) 或水平流向(rows)。垂直流向窗格具有较高的分界线,水平流向窗格具有较宽的分界线。Figure 1-10 是一个使用了数字图标的水平窗格例子。相反,垂直流向窗格中,第一列会包含1到4,第二列包含5到8。 Figure 1-10 Sample Horizontal Flow Pane Description of "Figure 1-10 Sample Horizontal Flow Pane" Gap属性来控制行和列直接的空间。padding属性来控制结点和网格窗格边缘的距离。Example 1-6 创建了一些列图标的水平流窗格。 Example 1-6 Create a Flow Pane 复制代码 1. FlowPane flow = new FlowPane(); 2. flow.setPadding(new Insets(5, 0, 5, 0)); 3. flow.setVgap(4); 4. flow.setHgap(4); 5. flow.setPrefWrapLength(170); // preferred width allows for two columns 6. flow.setStyle("-fx-background-color: DAE6F3"); 7. 8. ImageView pages[] = new ImageView[8]; 9. for (int i=0; i<8; i++) { 10.      pages[i] = new ImageView( 11.    new Image(LayoutSample.class.getResourceAsStream("graphics/chart_"+i+".png"))); 12.      flow.getChildren().add(pages[i]); 13. } 14. 15. border.setRight(flow);       // Add to BorderPane from Example 1-2 Example 1-6 把流窗格放到了边框窗格的右部。结果是Figure 1-11 . Figure 1-11 Flow Pane in a Border Pane Description of "Figure 1-11 Flow Pane in a Border Pane" 瓦片窗格TilePane 瓦片窗格和流窗格很类似,TilePane 布局窗格中的所有结点都大小相同,放在网格中。结点可以水平放置(in rows) 或垂直放置(in columns)。水平放置的瓦片在宽度宽度方向而垂直的在高度方向。使用prefColumns和 prefRows属性来设置瓦片窗格的首选大小。 Gap属性来控制行和列直接的空间。padding属性来控制结点和网格窗格边缘的距离。 Example 1-7 创建了一个水平瓦片窗格,其效果和Figure 1-10 相同。 Example 1-7 Create a Tile Pane 复制代码 1. TilePane tile = new TilePane(); 2. tile.setPadding(new Insets(5, 0, 5, 0)); 3. tile.setVgap(4); 4. tile.setHgap(4); 5. tile.setPrefColumns(2); 6. tile.setStyle("-fx-background-color: DAE6F3"); 7. 8. ImageView pages[] = new ImageView[8]; 9. for (int i=0; i<8; i++) { 10.      pages[i] = new ImageView( 11.    new Image(LayoutSample.class.getResourceAsStream("graphics/chart_"+i+".png"))); 12.      tile.getChildren().add(pages[i]); 13. } 锚窗格AnchorPane AnchorPane 布局窗格用来在窗格的上下左右中固定结点。当窗口大小改变时,结点会维持它们原来的相对位置。一个结点可以赋予多个位置,一个位置也可以赋予多个结点。Figure 1-12 是一个锚窗格,网格窗格在顶部,有两个按钮的HBox窗格在底部偏右。 Figure 1-12 Sample Anchor Pane Description of "Figure 1-12 Sample Anchor Pane" Example 1-8 照上面创建了一个锚窗格。 Example 1-8 Create an Anchor Pane 复制代码 1. AnchorPane anchorpane = new AnchorPane(); 2. Button buttonSave = new Button("Save"); 3. Button buttonCancel = new Button("Cancel"); 4. 5. HBox hbox = new HBox(); 6. hbox.setPadding(new Insets(0, 10, 10, 10)); 7. hbox.setSpacing(10); 8. hbox.getChildren().addAll(buttonSave, buttonCancel); 9. 10. anchorpane.getChildren().addAll(grid,hbox);   // Add grid from Example 1-5 11. 12. 13. 14. 15. 16. 17. 18. AnchorPane.setBottomAnchor(hbox, 8.0); 19. AnchorPane.setRightAnchor(hbox, 5.0); 20. AnchorPane.setTopAnchor(grid, 10.0); 21. 22. border.setCenter(anchorpane);       // Add to BorderPane from Example 1-2 Example 1-8 最后一行把锚窗格放在了边框窗格中间,取代了先前的结点。结果见Figure 1-13 . Figure 1-13 Anchor Pane in a Border Pane Description of "Figure 1-13 Anchor Pane in a Border Pane" 当窗口大小改变时,结点会维持它们原来的相对位置。 Figure 1-14 在按钮在底部,当窗口变低时,按钮也向上移动了。 Figure 1-14 Resized Anchor Pane 第三十回 重置JavaFX2.0结点 使用JavaFX内置布局窗格最大的好处是窗格帮你管理结点的大小和对其性质。窗格改变大小时,结点大小十分改变要根据窗格的性质。注意不是所有的结点类 都可以改变大小。UI控件和布局窗格可以,但是形状、Text对象、 Group对象不可以,它们在布局中是刚性对象。如果你想要更多的控制控件大小,请直接指定其尺寸。布局窗格会根据你的设置来决定控件的大小。 改变结点大小 布 局窗格通过调用prefWidth(height)和 prefHeight(width) 方法查询结点的首选尺寸。默认地,UI控件根据其内容计算它们的首选尺寸。比如,Button 对象的尺寸是根据文本长度和标签中字体的尺寸(可能还有图片)计算的。一般的,计算出来的尺寸都是刚好够大以使标签能完全看见。 UI控件也提供根据典型用法的默认最小和最大尺寸。比如,Button 对象的最大尺寸是首选尺寸,因为不太可能让其任意大。然而,ScrollPane 对象的最大尺寸是不确定的,因为总是希望它们会随着内容增长。 既可以使用结点的默认尺寸,也可以随心而设置。比如,Figure 2-1 是边框窗格中一些按钮和列表视图的默认尺寸。 Figure 2-1 Computed Sizes Description of "Figure 2-1 Computed Sizes" 如果想要 Figure 2-2 中的效果呢?里面UI控件的尺寸都是预设的。 Figure 2-2 Desired Sizes Description of "Figure 2-2 Desired Sizes" 应用程序通常需要直接设置控件的最大、最小、首选尺寸。下面将讲解如何修改控件的外观来是自己满意。 让按钮同样大 要实现该目标,可以为每个按钮指定高度和宽度,然后将它们的首选尺寸改成最大的宽度和高度。一种更简单的方法是让布局窗格来完成该工作。布局窗格由想要达到的效果来决定。 使用垂直盒子 Figure 2-1 中的场景为按钮使用了一个VBox布局窗格且按钮尺寸是默认的。按钮已经具有相同高度了,所以只要改变其宽度即可。 Figure 2-2 中的场景使用一个VBox窗格来利用一下默认行为:让 VBox窗格的宽度和最宽元素的宽度相同。要让按钮都和 VBox窗格宽度相同,就要把每个 按钮的最大宽度设为Double.MAX_VALUE常量,这样控件的变化就没有了限制。当然,把最大值指定为具体指也行,比如 80.0. Example 2-1 在实现了任何让VBox窗格中的按钮都一样宽。 Example 2-1 Set a Column of Buttons to the Same Width 复制代码 1. BorderPane root = new BorderPane(); 2. root.setPadding(new Insets(20, 0, 20, 20)); 3. 4. Button btnAdd = new Button("Add"); 5. Button btnDelete = new Button("Delete"); 6. Button btnMoveUp = new Button("Move Up"); 7. Button btnMoveDown = new Button("Move Down"); 8. 9. btnAdd.setMaxWidth(Double.MAX_VALUE); 10. btnDelete.setMaxWidth(Double.MAX_VALUE); 11. btnMoveUp.setMaxWidth(Double.MAX_VALUE); 12. btnMoveDown.setMaxWidth(Double.MAX_VALUE); 13. 14. VBox vbButtons = new VBox(); 15. vbButtons.setSpacing(10); 16. vbButtons.setPadding(new Insets(0, 30, 10, 25)); 17. vbButtons.getChildren().addAll(btnAdd, btnDelete, btnMoveUp, btnMoveDown); 18. 19. root.setright(vbButtons); 小提示: 把按钮列放在了边框窗格的右部就限制了最宽按钮的首选宽度。边框窗格的中部会尽量去填满所以的控件,所以如果把Vbox放在中间,它和按钮都会扩大。 使用瓦片窗格 Figure 2-1 中的场景为底部的按钮使用了一个HBox布局窗格且按钮尺寸是默认的。 按钮的款和搞都不同。 Figure 2-2 中的场景使用了一个水平TilePane布局窗格来利用其默认行为:每个细胞或瓦片都相同大小。 每个瓦片的尺寸都需要是瓦片窗格中最大结点的大小。 要让按钮和瓦片尺寸一样,把它们的最大宽度和高度设为Double.MAX_VALUE常量。 Example 2-2 中实现了该目标。 Example 2-2 Set a Row of Buttons to the Same Size 复制代码 1. Button btnApply = new Button("Apply"); 2. Button btnContinue = new Button("Continue"); 3. Button btnExit = new Button("Exit"); 4. 5. btnApply.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); 6. btnContinue.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); 7. btnExit.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); 8. btnExit.setStyle("-fx-font-size: 14pt;"); 9. 10. TilePane tileButtons = new TilePane(Orientation.HORIZONTAL); 11. tileButtons.setPadding(new Insets(20, 10, 20, 0)); 12. tileButtons.setHgap(10.0); 13. tileButtons.getChildren().addAll(btnApply, btnContinue, btnExit); 即使窗口改变大小瓦片也不会变,所以瓦片窗格中的按钮不会改变的。 让结点保持首选尺寸 当 舞台改变大小时,其中的布局窗格可能会给包含的控件产生过多或不足的空间。每个布局窗格都有其空间分派原则,根据的是控件最小、最大、首选尺寸。一般地, 最大尺寸是Double.MAX_VALUE的话控件会扩张来填充空间而有限定大小的就不会。比如, ListView对象最大值不能确定。如果要限制其首选高度,可以设置为 Control.USE_PREF_SIZE 常量,见Example 2-3 . Example 2-3 Set Maximum Height to Preferred Height 复制代码 1. ListView lvList = new ListView(); 2. ObservableList items = FXCollections.observableArrayList ( 3.         "Hot dog", "Hamburger", "French fries", 4.         "Carrot sticks", "Chicken salad"); 5. lvList.setItems(items); 6. lvList.setMaxHeight(Control.USE_PREF_SIZE); 按钮默认只扩张到首选尺寸。但是如果最小宽度没有指定,可能会出现三个点(... ) 。要防止按钮比首选宽度小,把最小宽度设置为其首选宽度。见Example 2-4 . Example 2-4 Set Minimum Width to Preferred Width Button btnMoveDown = new Button("Move Down"); btnMoveDown.setMinWidth(Control.USE_PREF_SIZE); 控件的首选尺寸首先基于经过计算(内容)的值。可以覆盖其首选值为自己的选择。下面的语句覆盖了列表的宽度。 lvList.setPrefWidth(150.0); 阻止改变大小 如果不想要结点改变大小,把其最大、最小、首选值设为一样的。要只阻止宽度或高度改变,就把宽度或高度的限制设为相同。在Example 2-5 中,列表创建时要求了限制值相同,所以即使窗口改变其尺寸也不变。一个按钮也限制了宽度的三个值相同。 Example 2-5 Set Size Constraints to Prevent Resizing 复制代码 1. ListView lvList = new ListView(); 2. lvList.setMinSize(150.0, Control.USE_PREF_SIZE); 3. lvList.setMaxSize(150.0, Control.USE_PREF_SIZE; 4. 5. Button btnDelete = new Button("Delete"); 6. btnDelete.setMinWidth(80.0); 7. btnDelete.setPrefWidth(80.0); 8. btnDelete.setMaxWidth(80.0); 文本对齐 每个布局窗格都有自己的方法来对其结点。比如,HBox和 VBox 布局窗格中,结点是顶端对齐和左对齐的。TilePane和 FlowPane窗格中,结点是居中的。窗格自身默认是顶端对齐和左对齐的。 可以使用setAlignment()方法设置结点和窗格的对其方式。对其常量位于 javafx.geometry包中的 enum类型中。 HPos - 指定水平对齐的值 Pos - 指定垂直和水平对齐的值。下划线左边指定的是垂直对齐值,下划线右边指定的是水平对齐值。比如Pos.BOTTOM_LEFT要结点垂直方向在底部而水平方向在左边。 VPos - 指定垂直对齐的值。 Figure 2-3 是代码  Example 2-6 的效果.由于没有对齐限制,布局窗格在左上角。 Figure 2-3 Default Positions Description of "Figure 2-3 Default Positions" Example 2-6 Create a UI with Default Alignment 复制代码 1. public void start(Stage primaryStage) { 2.     GridPane grid = new GridPane(); 3.     grid.setHgap(10); 4.     grid.setVgap(12); 5. 6.     HBox hbButtons = new HBox(); 7.     hbButtons.setSpacing(10.0); 8. 9.     Button btnSubmit = new Button("Submit"); 10.     Button btnClear = new Button("Clear"); 11.     Button btnExit = new Button("Exit"); 12.     btnSubmit.setStyle("-fx-font-size: 11pt;"); 13. 14.     Label lblName = new Label("User name:"); 15.     TextField tfName = new TextField(); 16.     Label lblPwd = new Label("Password:"); 17.     PasswordField pfPwd = new PasswordField(); 18. 19.     hbButtons.getChildren().addAll(btnSubmit, btnClear, btnExit); 20.     grid.add(lblName, 0, 0); 21.     grid.add(tfName, 1, 0); 22.     grid.add(lblPwd, 0, 1); 23.     grid.add(pfPwd, 1, 1); 24.     grid.add(hbButtons, 0, 2, 2, 1); 25.     Scene scene = new Scene(grid, 250, 250); 26.     primaryStage.setTitle("Layout Positioning"); 27.     primaryStage.setScene(scene); 28.     primaryStage.show(); 29. } 如果要 Figure 2-4 中的效果呢?窗格在屏幕中央。 Figure 2-4 Desired Positions Description of "Figure 2-4 Desired Positions" 下面就讲述任何覆盖默认位置设置。 将网格居中 要让 Example 2-6 中的网格居中,这样做: grid.setAlignment(Pos.CENTER); 对齐列中的控件 在我们的理想布局中,标签要右对齐而文本框左对齐。要在网格中达到该目的,为每列使用ColumnConstraints类并设置水平对其方式。 Example 2-7 为 Example 2-6 中的网格定义了列属性。 Example 2-7 Define the Columns in the Grid 复制代码 1. GridPane grid = new GridPane(); 2. grid.setAlignment(Pos.CENTER); 3. grid.setHgap(10); 4. grid.setVgap(12); 5. 6. ColumnConstraints column1 = new ColumnConstraints(); 7. column1.setHalignment(HPos.RIGHT); 8. grid.getColumnConstraints().add(column1); 9. 10. ColumnConstraints column2 = new ColumnConstraints(); 11. column2.setHalignment(HPos.LEFT); 12. grid.getColumnConstraints().add(column2); 13. 14. 使用VBox窗格也可以实现控件右对齐。照下面这样使用 setAlignment() 方法: 15. VBox vbox = new VBox; 16. vbox.setAlignment(Pos.CENTER_RIGHT); 按钮居中 HBox中的按钮跨越了网格的列。下面的代码让按钮居中: HBox hbButtons = new HBox() hbButtons.setAlignment(Pos.CENTER); HBox 的setAlignment() 方法居中了HBox窗格和其中的结点。要是更喜欢将 HBox窗格行居中而其中的按钮底部对齐,就像 Figure 2-5 . Figure 2-5 Override Positions and Bottom-Justify the Buttons Description of "Figure 2-5 Override Positions and Bottom-Justify the Buttons" 要这样安排,把 HBox 窗格放在一个只有一个细胞的内网格中,再把它放在外网格的第三行。把内网格设置为居中,把HBox窗格设为底部对齐,见 Example 2-8 . Example 2-8 Center and Bottom-Justify the Buttons 复制代码 1. HBox hbButtons = new HBox(); 2. hbButtons.setSpacing(10.0); 3. hbButtons.setAlignment(Pos.BOTTOM_CENTER); 4. hbButtons.getChildren().addAll(btnSubmit, btnClear, btnExit); 5. 6. GridPane innergrid = new GridPane(); 7. innergrid.setAlignment(Pos.CENTER); 8. innergrid.add(hbButtons, 0, 0); 9. grid.add(innergrid, 0, 2, 2, 1); JavaFX高级教程:JavaFX2.0的FXML语言 FXML是JavaFX 2.0新引入的。你可能会问"What is FXML?" 和"Is FXML for me?" FXML 是基于XML的一种声明性标记语言,用来定义应用的用户接口。FXML对于定义静态的布局很便利,诸如form, control, 和table。使用FXML也可以动态构造布局,不过要结合脚本。 FXML是一个优势是基于XML,所以多数开发者,尤其是web开发者和其他RIA平台的开发者会很熟悉它。另一个优势是FXML不是编译型语言,不需要 编译后才能看出变化。第三个好处是可以很简单的看到应用场景的结构。反过来,也就很简单地可以在组内进行合作开发用户接口。 要对比JavaFX和FXML,看图Figure 1   .构成该应用的场景包括一个边框布局,在它的顶部和中间各有一个标签。 Figure 1 Border Pane Simple Example Description of "Figure 1 Border Pane Simple Example"   Example 1   是相应的JavaFX代码. Example 1 JavaFX Scene Graph BorderPane border = new BorderPane(); Label toppanetext = new Label("Page Title"); border.setTop(toppanetext); Label centerpanetext = new Label ("Some data here"); border.setCenter(centerpanetext); Example 2   是相应的FXML. Example 2 FXML Scene Graph                   
        
展示FXML优势的最好方法是例子。本指南讲解如何创建Figure 2   中的登陆界面 . Figure 2 Login User Interface Description of "Figure 2 Login User Interface"   开始之前先熟悉一下Figure 3   中的用户接口. 该接口使用了一个包含两部分的边框布局。顶区域包括一个堆栈布局,里面是用文本Label Example覆盖一副图片。   中部区域是一个网格布局,有标签、文本框、密码框和按钮。 Figure 3 Layout of Login User Interface Description of "Figure 3 Layout of Login User Interface"   要创建该界面,我们需完成以下任务: Prepare for This Tutorial Set Up the Project Set Up the Application Basics Create the Properties File Create the FXML File Define a Border Pane Layout Stack Text Over an Image Add a Grid Layout and Controls Add a Button Event Use a Scripting Language Use a Style Sheet 准备 本指南使用NetBeans IDE.请确保NetBeans IDE的版本支持JavaFX 2.0。 要完成本文,应熟悉用JavaFX编用户接口。尤其要知道场景的知识,因为FXML的语法结构和JavaFX的场景很像。 建立工程 第一步是建立JavaFX工程。 从File菜单选择   New Project   . 在  JavaFX   application category中,选择JavaFX FXML Application,点   Next   . 输入名称 FXMLExample点击  Finish   . NetBeans IDE打开了工程。有三个文件:FXMLExample.java, Sample.fxml, and Sample.java. 下载浅蓝色渐变的  fx_boxback.jpg   图片到桌面,用来当背景。然后把它拖到fxmlexample文件夹下。 建立基础 每个 FXML 应用必须包含一些JavaFX代码,最少也有创建舞台和场景还有启动的代码。 打开FXMLExample.java   ,删除NetBeans IDE生成的代码,用下面的代码代替。Example 3   . Example 3 FXMLExample.java package fxmlexample; 复制代码 1. import java.util.ResourceBundle; 2. import javafx.application.Application; 3. import javafx.fxml.FXMLLoader; 4. import javafx.scene.Parent; 5. import javafx.scene.Scene; 6. import javafx.stage.Stage; 7. 8. public class FXMLExample extends Application { 9.     @Override 10.     public void start(Stage stage) throws Exception { 11.         stage.setTitle("FXML Example"); 12.          13.         Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"), 14.             ResourceBundle.getBundle("fxmlexample.fxml_example"));         15.         Scene scene = new Scene(root, 226, 264); 16.         stage.setScene(scene); 17.         stage.show(); 18.     } 19. 20.     public static void main(String[] args) { 21.         launch(args); 22.     } 23. } 作为一个JavaFX编程者,应该很熟悉创建舞台和场景。而然,这一行是FXML特有的: Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"), ResourceBundle.getBundle("fxmlexample.fxml_example")); FXMLLoader.load() 方法从文件   fxml_example.fxml加载了目标层次并分配给变量root。 getResources参数增加了一个资源束 来为用户接口具体化,使得诸如定位任务更简单。后面将使用一个配置文件回到资源束并创建FXML源文件。 总之,场景已经创建,root变量是场景根元素。 FXML的根元素标记为场景的根。 创建配置文件 最佳实践是将字符串外部化,把它们放进配置文件。按照以下步骤创建用户登录界面的配置文件。 在Projects窗口,右击 fxmlexample文件夹,选择New   ,然后是Other   . 在New File对话框,点击Other   ,然后是Properties File   ,点  Next   . 输入fxml_example作为名称,点Finish。 IDE打开了该文件,其扩展名是.properties。 输入资源名称和值,如 Example 4   . Example 4 Resource names in fxml_example.properties loginExample=Login Example signIn=Sign in: userName=Username: password=Password: submit=Submit 创建FXML文件 现在创建fxml_example.fxml文件并插入XML声明和导入语句。 在Projects窗口,右击Sample.fxm,选择Rename   . 输入fxml_example后点OK   . 打开fxml_example文件,删除自动生成代码,用下面的取代 Example 5   . Example 5 Declaration and Import Statements 所有FXML文件必须以XML声明开始。它定义了XML版本(1.0)和编码类型(UTF-8). 在JavaFX中,类名称可以被完整描述(包括包名),或者是使用导入语句,见 Example 5   .只要你喜欢,你可以完整导入所以相关类。 定义边框布局 下面开始构建用户界面。在导入语句后面插入下面代码 Example 6   . Example 6 Border Pane Layout               
    
本例中,场景根是BorderPane布局类,定义了两快区域,顶部和中部。 fx:controller属性定义了控制器文件,必须要声明在FXML根元素中。后面还会更多的了解控制器。 xmlns:fx="http://javafx.com/fxml"属性将命名空间映射到 http://javafx.com/fxml。命名空间也必须声明在FXML根元素中。该属性让你可以使用JavaFX API相关的FXML标签。 在图像上覆盖文字 现在,在边框布局的顶区域放置一个堆栈布局。该布局包括一个标签和被覆盖的图片,见 Figure 4   . Figure 4 Top Region of Border Pane, Including Stack Pane Description of "Figure 4 Top Region of Border Pane, Including Stack Pane"   堆栈布局的代码是 Example 7   .把它们插入在    和  标签之间。 Example 7 Stack Text Over an Image                                                                                                                                                      StackPane布局将其孩子结点放进一个栈中,后面的会放在之前的上面。该布局可以很方便的把文本放在图片上。   标签把孩子结点加入到场景中,就像使用getChildren().add()方法。 Image类加载了图片 fx_boxback.jpg,图片放在当前FXML文件路径下。 ImageView类用来显示图片。 Label类有一个文本属性来自资源 loginExample。在 FXML中,资源名称使用%来指定。加载时, FXML 加载器使用 loginExample  的值Login Example来取代它。 FXML定义风格时很像setStyle()方法的CSS风格。 在Example 7  中 Label类使用  style  标签来设置字型为normal,字体是Tahoma,字号是20。 另一种定义风格的方法和Java一样,加载样式表。使用样式表的话对于以后修改比较方便。看本文的 《使用样式表》  了解相关信息。 添加网格布局和控件 下面在边框布局的中部添加网格布局。使用网格布局可以在屏幕上垂直和水平的放置控件。见  Figure 5   . Figure 5 Grid Pane in Center Region of Border Pane Description of "Figure 5 Grid Pane in Center Region of Border Pane"   Example 8  是网格布局的代码,放在
和  
  标签中。 Example 8 Grid Layout with Controls