本文档介绍 JavaFX 语言中可用的各种 GUI 组件,提供示例代码并讨论 JavaFX 组件与 Swing GUI 组件的差异。
先决条件
如果您以前没有阅读过 JavaFX 入门 教程,那么我们强烈建议首您先阅读该教程,然后再学习本文档。
您还应该对 Java 编程语言有基本的了解,尤其是使用 Swing 和 Java 2D 编程。
比较声明式语法与过程式语法
关爱的故事JavaFX
语言提供了声明式语法,用于表达用户界面组件的结构和内容。为了帮助您理解这些内容,我们用纯过程的方式重新编写了上面的程序,就像我们在编写
Swing 程序时经常做的那样:
var win = new Frame(); win.title = "Hello World JavaFX"; win.width = 200; var label = new Label(); = "Hello World"; t = label; win.visible = true;上面的源代码还是有效的 JavaFX 程序并且它的效果与在《JavaFX 入门》教程中创建的程序的效果完全相同。
下面概述了在声明式方法和过程式方法的实际过程:
调用 Frame 类构造函数以创建一个新的 Frame
为标题、宽度、可见性以及内容属性赋值。
在为内容属性赋值的过程中,调用 Label 类构造函数以创建一个新的 Label 并为其文本属性赋值。
但是,即便是这样一个非常简单的示例,我们也可以很容易掌握声明式语法程序的含义。
因此,声明式编程
需要通过单一表达式中创建程序。比如说第一个“HelloWorld”示例应用程序,该表达式的本质就是一个对象分配表达式(构造函数),用于生成程序的对象图。
返回顶部
添加动态行为
“Hello World”程序没有动态行为。要使用 JavaFX
创建具有动态行为的图形用户界面,应该让所创建的图形用户界面组件的属性依赖于其他
对象的属性值。这些其他对象可以代表您觉得适合的应用程序的各种状态。由于 GUI 组件的属性依赖于其他对象的属性,因此,GUI
组件将自动反映用户对其他对象的修改。在这种情况下,通常 GUI 组件将视图 和“其他”对象 称为模型。下面是模型/视图版本的“Hello
World”程序。
import javafx.ui.*; class HelloWorldModel { attribute saying: String; } var model = HelloWorldModel { saying: "Hello World" }; Frame { title: "Hello World JavaFX" width: 200 content: Label { text: bind model.saying } visible: true };运行该程序将显示以下内容:
如果将 model 对象的 saying 属性
修改为以下内容,
model.saying = "Goodbye Cruel World!";则视图会自动发生相应的变化,如下图所示:
注意,该示例对标签的 text 属性进行了初始化,方法是将 JavaFX bind 操作符应用于 model 的 saying
属性。此处,bind 操作符表示增量式 更新。这意味着只要 model.saying 的值更改,标签的 text 属性就更新为相同的值。
在一些输入小部件中(如 Buttons、CheckBoxes 和 TextFields),模型属性和 GUI
组件属性之间的链接可能是双向的。
考虑下面这个示例:
import javafx.ui.*; class HelloWorldModel { attribute saying: String; } var model = HelloWorldModel { saying: "Hello World" }; Frame { title: bind "{model.saying} JavaFX" width: 200 content: TextField { value: bind model.saying } visible: true };运行该程序将显示以下内容:
如果在 Input 文本字段中键入其他内容并按下回车键,则窗口的标题将会发生相应的变化:
在本例中,文本字段的 value 属性将根据用户的输入结果(通过实现 TextField 类)进行更新。
此时,model 的 saying 属性将更新为与之相同的值。由于赋值给窗口的 title 属性的表达式依赖于 model 的
saying 属性,因此会重新计算该表达式并使用结果更新窗口的 title 属性。
注意:您可以 将任何组件的任何属性
bind(绑定)到增量式计算的表达式。这样的表达式可以使用条件逻辑、迭代、选择来生成任何复杂程度的动态内容。但是,此类内容的表达式仍然是声明式的。
返回顶部
进一步了解 JavaFX GUI 组件
本部分将介绍 JavaFX 语言中可用的各种 GUI 组件,并且提供了它们的使用示例。
还与 Swing GUI 组件进行了比较。
Border 和 Layout Manager
quoia
Menu
Label
Group Panel、Simple Label 和 TextField
Button
ListBox
SplitPane
RadioButton、RadioButtonMenuItem、ToggleButton 和 ButtonGroup
ComboBox
Tree
Table
TextComponent
Spinner 和 Slider
返回顶部
Border 和 Layout Manager
在 JavaFX 语言中,边框(Border)和布局管理器(Layout Manager)的使用也是声明式的。每个 Swing/AWT
布局管理器都封装在一个 JavaFX 类中,并使用指定的布局管理器实例化 JPanel。使用 JavaFX
莎拉 佩林类提供的属性采用声明式方法向(底层)
JPanel 中添加组件。每个 Swing 边框类型封装在 JavaFX
类中,它具有与该边框的配置选项相对应的属性。以下两个简单的示例演示了如何使用 EmptyBorder 和
GridPanel。也许您已经猜到了,EmptyBorder 对应于 javax.swing.border.EmptyBorder 而
GridPanel 对应于 java.awt.GridLayout。
import javafx.ui.*; class ButtonClickModel { attribute numClicks: Number; } var model = new ButtonClickModel(); Frame { width: 200 content: GridPanel { border: EmptyBorder { top: 30 left: 30 bottom: 30 right: 30 } rows: 2 columns: 1 vgap: 10 cells: [Butt
on { text: "I'm a button!" mnemonic: I action: operation() { model.numClicks++; } }, Label { text: bind "Number of button clicks: {model.numClicks}" }] } visible: true };运行该程序将显示以下内容:
单击几次按钮之后,将显示以下内容:
注意:Button 的 action 和 mnemonic 属性将在下文中讨论。
在本例中,GridPanel 具有一列、两行并且行间距为 10 像素,配置方法是对其 columns、rows 和 vgap
属性赋值。如果要指定列间距,GridPanel 还提供了一个 hgap 属性。
所有空白边框都设置为 30 像素。并通过指定 cells 属性添加了按钮和标签。
GridPanel 实现针对 cells 属性的插入和删除对底层 JPanel 执行相应的修改(即插入或删除组件)。JavaFX
还以类似的方式支持其他一些布局管理器。下表给出了这些布局管理器的摘要:
miryo
JavaFX 小部件布局管理器
GridPanelGridLayout
GridBagPanelGridBagLayout
FlowPanelFlowLayout
四眼天鸡插曲BorderPanelBorderLayout
BoxBoxLayout
StackPanelRomain Guy's StackLayout
CardPanelCardLayout
GroupPanelorg.jdesktop.layout.GroupLayout
下面概述了与之对应的 JavaFX 边框和 Swing 边框:
冬天服装搭配
JavaFX 边框Swing 边框
EmptyBorderEmptyBorder
LineBorderLineBorder
BevelBorderBevelBorder
SoftBevelBorderSoftBevelBorder
MatteBorderMatteBorder
TitledBorderTitledBorder
top
Menu
让我们向上面的示例中添加一个简
单的菜单栏。新代码如以下粗体所示:
import javafx.ui.*; import java.lang.System; class ButtonClickModel { attribute numCli
cks: Number; } var model = new ButtonClickModel(); Frame { width: 200 menubar: MenuBar { menus: Menu { text: "File" mnemonic: F items: MenuItem { text: "Exit" mnemonic: X accelerator: { modifier: ALT keyStroke: F4 } action: operation() { it(0); } } } } content: GridPanel { border: EmptyBorder { top: 30 left: 30 bottom: 30 right: 30 } rows: 2 columns: 1 vgap: 10 cells: [Button { text: "I'm a button!" mnemonic: I action: operation() { model.numClicks++; } }, Label { text: bind "Number of button clicks: {model.numClicks}" }] } visible: true }运行该程序并按 ALT+F 键,将显示以下内容:
如您所见,上述代码创建了一个菜单栏,方法是为窗口的 menubar 属性指定一个 MenuBar 类的新实例。
延续英文通过指定菜单栏的 menus 属性可以向菜单栏中添加菜单。在本例中,我只添加了一个菜单,但是也可以通过任意表达式返回 Menu
对象列表。
要定义菜单,可以对它的text、mnemonic 和 items 属性赋值。
您可能会说,text 属性的类型为 String。但是 mnemonic 属性的类型为 KeyStroke。其值 F 是
KeyStroke 类的一个枚举实例。在 JavaFX 中,无需符合类型名称(在别处您必须将 F 引用为
F:KeyStroke)即可访问属性 initializer 的上下文中该属性的静态类型(以及本例的 Java 类中的静态字段)枚举值。
以上代码创建了一个菜单项 MenuItem,其 text 属性为“Exit”并且其 mnemonic 属性为 X。还为其
accelerator 属性赋给一个值。请注意,在该值的声明中忽略了类型名称 Accelerator。
这是允许的。如果类型名称不存在,则使用属性的静态类型,在本例中为 Accelerator。此外,Accelerator 的
modifier 和 keyStroke 属性已经使用初始化为枚举类型。最后,还为 MenuItem 的 action 属性添加了
一个
function 类型(即,其值为函数,而不是对象)。在本例中,operation 子过程的作用是调用一些已经
编写好的 Java
代码退出应用程序。
好声音张新返回顶部
Label
no politics today
JavaFX Label 类支持 HTML 内容。您可以使用 Label 创建具有 HTML 和 CSS 样式的文本和图像,这与典型的
Web 应用程序非常相似。此外,通过使用嵌入的 JavaFX 表达式,您可以在 Swing 应用程序中轻易地创建动态 HTML
内容,就像网页作者所使用的工具(如 JSTL 或 Velocity)一样。
考虑下面这个购物车示例:
import javafx.ui.*; class Item { attribute id: String; attribute productId: String; attribute description: String; attribute inStock: Boolean; attribute quantity: Number;
attribute listPrice: Number; attribute totalCost: Number; } alCost = bind quantity*listPrice; class Cart { attribute items: Item*; attribute subTotal: Number; } operation sumItems(itemList:Item*) { var result = 0.00; for (item in itemList) { result += alCost; } return result; } attribute Cart.subTotal = bind sumItems(items); var cart = Cart { items: [Item { id: "UGLY" productId: "D100" description: "BullDog" inStock: true quantity: 1 listPrice: 97.50 }, Item { id: "BITES" productId: "D101" description: "Pit Bull" inStock: true quantity: 1 listPrice: 127.50 }] }; Frame { content: Label { text: bind "<html> <h2 align='center'>Shopping Cart</h2> <table align='center' border='0' bgcolor='#008800' cellspacing='2' cellpadding='5'> <tr bgcolor='#cccccc'> <td><b>Item ID</b></td> <td><b>Product ID</b></td> <td><b>Description</b></td> <td><b>In Stock?</b></td> <td><b>Quantity</b></td> <td><b>List Price</b></td> <td><b>Total Cost</b></td> <td> </td> </tr> { if (sizeof cart.items == 0) then "<tr bgcolor='#FFFF88'><td colspan='8'><b>Your cart is empty.</b></td></tr>" el foreach (item in cart.items) "<tr bgcolor='#FFFF88'>
>unix