Sun打算让
JavaFX提供比Java更多的快速用户接口开发功能。同时,这个脚本语言可
以被使用在应用需要的各个方面。在本文的例子中使用
JavaFX开发了一个用户接口,一个登录窗口。本例还使用了Java程序实现了同一个登录窗口。这两个文件的代码如下:
import
JavaFX.ui.*;
Frame {
title: "Authenticate Please"
width: 300
height: 125
visible: true
content: GridBagPanel {
border: EmptyBorder {
top: 5
left: 5
bottom: 5
right: 5}
cells: [GridCell {
anchor: EAST
gridx: 0
gridy: 0
insets: {top: 2}
content: SimpleLabel {text: "Username: "}},
GridCell {
anchor: WEST
fill: HORIZONTAL
weightx: 1
gridx: 1
gridy: 0
content: TextField {}},
GridCell {
anchor: EAST
gridx: 0
gridy: 1
insets: {top: 2}
content: SimpleLabel {text: "PasswordField: "}},
GridCell {
anchor: WEST
fill: HORIZONTAL
gridx: 1
gridy: 1
weightx: 1
content: PasswordField {}},
GridCell {
gridx: 1
gridy: 2
content: Button {text: "Login"}}]}
}
用Java实现同样的登录窗口的代码:
public class Login extends javax.swing.JFrame {
/** Creates new form Login */
public Login()
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
private void initComponents() {
usernameField = new javax.swing.JTextField();
usernameLabel = new javax.swing.JLabel();
passwordLabel = new javax.swing.JLabel();
passwordField = new javax.swing.JPasswordField();
loginButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Authenticate Please");
setName("loginFrame");
usernameField.setColumns(15);
usernameLabel.setText("Username:");
passwordLabel.setText("Password:");
passwordLabel.setRequestFocusEnabled(false);
passwordLabel.getAccessibleContext().setAccessibleName("Password:");
loginButton.setLabel("Login");
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
.add(usernameLabel)
.add(19, 19, 19))
.add(layout.createSequentialGroup()
.add(passwordLabel)
.add(23, 23, 23)))
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false)
.add(passwordField)
.add(usernameField)))
.add(layout.createSequentialGroup()
.add(147, 147, 147)
.add(loginButton)))
.addContainerGap(92, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(usernameField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(usernameLabel))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(passwordLabel)
.add(passwordField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 19, Short.MAX_VALUE)
.add(loginButton)
.addContainerGap())
);
pack();
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Login().setVisible(true);
}
});
}
// Va
RIAbles declaration - do not modify
private javax.swing.JButton loginButton;
private javax.swing.JPasswordField passwordField;
private javax.swing.JLabel passwordLabel;
private javax.swing.JTextField usernameField;
private javax.swing.JLabel usernameLabel;
// End of va
RIAbles declaration
}
从上面的代码可以看出,
JavaFX和传统的Java代码有些类似的地方,如类名上,但从总体结构上看还是有很大的差别。图5显示了这两种方式建立的登录窗口,上面的是用
JavaFX建立的,下面的是用传统的Java代码建立的。
图5 登录窗口:
这个小例子很清楚地揭示了
JavaFX和传统的Java的第一个主要的不同。
JavaFX使用了类似于HTML的语法形式来建立用户接口。在
JavaFX的语法中,开发人员只是定义了应该显示些什么东西,而不是用程序来指定需要如何建立。很显然,象
JavaFX风格(有时这种风格也叫做声明语法)的语法更容易建立维护。
当然,在
JavaFX中也可以使用类似于Java的方式来建立相同的登录窗口。如下面的代码所示:
import
JavaFX.ui.*;
var frame = new Frame();
frame.title = "Authenticate Please";
frame.width = 300;
frame.height = 125;
var panel = new GridBagPanel();
var border = new EmptyBorder();
border.bottom=5;
border.top=5;
border.left=5;
border.right=5;
panel.border = border;
var inset = new Insets();
inset.top=2;
var gcUserLabel = new GridCell();
gcUserLabel.anchor = EAST;
gcUserLabel.gridx=0;
gcUserLabel.gridy=0;
gcUserLabel.insets=inset;
var usernameLabel = new SimpleLabel();
usernameLabel.text = "Username: ";
gcUserLabel.content = usernameLabel;
var gcUserField = new GridCell();
gcUserField.anchor = WEST;
gcUserField.fill=HORIZONTAL;
gcUserField.weightx=1;
gcUserField.gridx=1;
gcUserField.gridy=0;
gcUserField.insets=inset;
gcUserField.content = new TextField();
var gcPassLabel = new GridCell();
gcPassLabel.anchor = EAST;
gcPassLabel.gridx=0;
gcPassLabel.gridy=1;
gcPassLabel.insets=inset;
var passwordLabel = new SimpleLabel();
passwordLabel.text = "Password: ";
gcPassLabel.content = passwordLabel;
var gcPassField = new GridCell();
gcPassField.anchor = WEST;
gcPassField.fill=HORIZONTAL;
gcPassField.weightx=1;
gcPassField.gridx=1;
gcPassField.gridy=1;
gcPassField.insets=inset;
gcPassField.content = new PasswordField();
var gcButton = new GridCell();
gcButton.gridx=1;
gcButton.gridy=2;
var loginButton = new Button();
loginButton.text = "Login";
gcButton.content = loginButton;
panel.cells = [gcUserLabel, gcUserField, gcPassLabel, gcPassField, gcButton];
frame.visible = true;
frame.content = panel;七、执行
JavaFX程序
JavaFX应用程序在桌面上可以被FXShell执行。FXShell是一个在OpenJFX上提供的一个类。为了在NetBeans中运行
JavaFX文件,在Projects窗口中右击,然后选择Properties。在打开的Project Properties窗口(如图6)中,在Categories列表中选择Run,然后输入net.java.
JavaFX.FXShell作为Main类,以及我们要运行的
JavaFX程序的名子,在这里是Login.fx。
图6 在NetBeans中运行FXShell
为了在Eclipse中运行
JavaFX,右击在Package ExplorerView中的
JavaFX文件,选择"Run As"->"Run...",如图7所示:
在打开Run对话框后,从左侧的列表中选择
JavaFX应用程序,然后按New launch configuration图标。最后"New_configuration"被建立。选中New_configuration,然后点Run来运行
JavaFX程序。
八、Java类、对象和自动数据绑定
JavaFX不仅限于用户接口,它还可以定义类。在接下来的例子中,我们将建立个用于计算投资的计算器。这个用户接口允许用户输入最初资金、年汇报率,返还比率和投资时间来计算投资汇报率(界面如图8所示),源程序的代码可参考InvestmentCalculator.fx文件。
图8 投资计算器界面.
在这个程序中我们定义了一个InvestmentTool类。在InvestmentTool类中的每一个属性定义将使用如下的语法:
attribute AttributeName : AttributeType Cardinality;
在
JavaFX中,"methods"被定义为functions或operations。一个函数是一个只包含一系列变量表达式和返回表达式的方法。如下面代码定义了一个计算直角三角形斜边长度的函数:
function hypotenuseLength (side1, side2){
var a = side1 * side1;
var b = side2 * side2;
var c = Math.sqrt(a+b);
return c;
}
一个操作,换句话说,就是可以包含任意数量表达式(包括变量声明、循环、条件等)。另外,也可以和JavaScript的操作和函数类似,可以先是一个类对象,但无需和类发生关系。
InvestmentTool计算器的实例和var calculator = InvestmentTool表达式有关。当
JavaFX类没有显式声明构造方法时,这个实例将使用如上的符号创建。
那么
JavaFX类和objects如何被使用呢?如InvestmentTool实例如何被一个
JavaFX用户接口使用呢?我们可以使用
JavaFX强有力的自动数据绑定来处理这个问题,InvestmentTool的属性值可以和用户接口组件相关联。当对象变化时,组件值就跟着变化。在下面的代码中将演示如何进行数据绑定(注意下面代码的bind关键字的使用),更详细的代码可以查看InvestmentCalculator.fx。
Spinner {
min: 0
max: 1000000
stepSize: 100
value: bind calculator.contributionAnnual
font: Font{faceName: "A
RIAl", size: 18}
九、触发器
自动绑定将使用户接口和对象同步。但如何处理象本例子的事件呢?我们可以注意到,在界面上没有按钮来响应用户的动作。
JavaFX的另一个特性就是这个本节要讲的触发器。触发器是数据编辑时引发的事件。它们类似于监听方法,但是要比使用监听方法更简单。在这个例子中,触发器和属性的更新变化进行绑定。也就是说,在任何时候,INvestmentTool实例的属性的值发生改变,"update"触发器就会被引发,并调用getReturn()方法。代码如下:
trigger on InvestmentTool.numberOfYears = value
然而,一个触发器可以被安装在任何类型上来监视它们的变化。触发器甚至还可以被安装在实例创建甚至在多值属性(如数组)的插入、替换或删除上。
十、静态类型
当我们在查看InvestmentTool类的定义时,类的属性都是静态类型的。因此,在
JavaFX中和JavaScript不同,
JavaFX是一种静态类型语言。类似JavaScript,我们也可以不声明变量的类型,在这种情况下,变量就被指定为所使用的类型。在下面的代码中x将被指定为Integer类型:
var x;
x = 3;
然而,由于它是静态类型,变量一但指定类型,在
JavaFX中这个变量就不能被赋予其
他类型的值。如下面的代码在JavaScript中是合法的,但在
JavaFX中,将会抛出"IncompatiableTypes"编译错误。
var x;
x = 3;
x = "hello";
在类、对象中,
JavaFX支持四种初级类型:String、Boolean、Number和Integer。这些分别和Java的java.lang.String, java.lang.Boolean, java.lang.Number,以及和byte, short, int, long, 或 java.math.BigInteger中的任何类型对应。
十一、在
JavaFX中使用Java 类
我们可以从上面的代码中已经注意到了getReturn()或hypotenuseLength(a, b)方法,这两个方法都是java的java.lang.Math类方法的引用。为了引用java类,在InvestmentCalculator.fx中包含了import表达式,代码如下所示:
import
JavaFX.ui.*;
import java.lang.Math;
事实上,
JavaFX可以导入所有的Java类和接口,就象它们在标准Java应用程序中使用的一样。
JavaFX的支持者确信这个特性可以使
JavaFX重用很多已经存在的Java代码,这样可以降低学习另一种脚本语言的曲线。
十二、
JavaFX仍然年轻
从上面的代码可以看出,用
JavaFX开发GUI程序,即快速,又容易。几乎和大名鼎鼎的SWING具有同样的功力。同时由于它可以使用Java的资源变得更加强大和更有弹性。但
JavaFX仍然非常年轻,它仍然有很多的路要走。我们在采用
JavaFX实现自己的项目之前还需要仔细了考虑一下,是否真的有必要。就
JavaFX目前的状态,仍处在测试阶段,这种语言已经被暗示并不能完全使开发人员解决他们未解决的问题。而
JavaFX现在存在的目的就是为了使已经存在的Java技术变得更容易使用。
到现在为止,
JavaFX还没有可视化的开发环境。对于一种定位于使用户满意的用户接口和
RIA开发需要的语言或技术,就必须有相关的可视化环境,否则毫无意义。而Chris Oliver也在他的weblog中提到,虽然我现在并未开时间来开发一个强大的支持
JavaFX的开发环境,但一但它的语言和API未定后,我将会为其披上华丽的外衣。
现在关于
JavaFX的开发文档和IDE支持还非常少。即使支持NetBeans和Eclipse的插
件的功能和数量也非常有限,如只有简单的编程特性。而一些高级的特性,如语法颜色高亮显示和代码格式化现在还没有。而唯一的高级特性“代码助手”的功能也十分有限。图9是使用插件编辑
JavaFX代码的截图。