前言
我在开始学习JavaFx时也尝试寻找有没有好的中文教程帮我全面了解和掌握JavaFX语言,并使我可以在短时间内将其应用于实践工作中。通过Google和百度寻找的的JavaFX的中文教程很少,大部分都是简单重复载录,唯一我在JavaFX的官网上看到一篇带我入门的中文教程,学习完后我总感觉还不是很全面,我需要一个再稍深入一点的资料,可惜只有一下英文的,无奈之下只好开始一点点“啃读”。后来一天我突然想,也许国内其它JavaFX的爱好者在入门时也会遇到我统一的问题,何不将我的学习笔记整理整理给予发表,也希望可以帮助帮助JavaFX爱好者入门,于是我就开始写这部书了。
我本人不喜欢太古板的学习方式,喜欢讲解和动手相结合,这样感觉印象深刻。所以基本上本书的编写也将会带有这种风格。不过第一次写,因此如有不妥之处还请大家批评指正。
本书作为入门级的材料,但仍然我需要假定读者具备一下一些程序开发知识点:
1、掌握JAVA语言基本知识;
2、掌握面向对象的基本知识;
3、掌握脚本语言,如javas
4、掌握Netbeans IDE开发工具使用知识;
如果以上知识点读者有不掌握的,还请读者自行阅读相关数据和材料进行补充,因为后面的书中将会涉及到以上知识点。
本书采用循序渐进的方式,从JavaFX的基础知识逐步进行深入,期间将结合讲解的知识点给出一下很小的示例程序以说明实际使用方法。本书章节大致分成两部分:其中之一是JavaFX的基本语法部分;其二是常用的JavaFX的API介绍。
好,书归正传,让我们从下面开始。
第一章 JavaFX基本语法
一切从基础开始,奠定好的基础将成就辉煌的“建筑”。JavaFX是一种编译型的脚本语言,它具备脚本语言的特点,同时又具有如Java语言一样面向对象的概念,这就意味着JavaFX可以采用面向对象的思想进行程序设计,可以有继承、封装、多态的特性。JavaFX程序的目标是实现适度大小的具备交互的具有图形界面的应用程序。
第一节 数据类型和值
数据类型是对某一数据所属的一种分类的描述,如字符串数据类型,其描述的的就是一个有若干字符相互拼接而形成的一种独特的类型。又如整型这种数据类型,描述的的是一种存储如1、2、3、4、5…..这样一种数据的独特类型。和任意一种语言一样(包括如javas
在JavaFX中数据类型的概念比较宽泛,如每一个变量,每一个函数,每一个表达式都是一种数据类型。在JavaFX中定义变量时可以明白的显示的去声明该变量的数据类型,也可以不声明。如果变量没有明白的显示的去什么变量的数据类型,那么JavaFX将依据上下文来推测决定改变量的数据类型。同样的,在JavaFX中函数的各个参数包括函数返回值都可以进行显示的数据类型声明,也可以不声明,如果函数的参数和函数的返回值没有声明其数据类型,则JavaFX也将依据上下文推断其参数或者返回值的数据类型。通过显示声明可以决定变量可以存储的值的类型和存储大小。
JavaFX的类型定义格式如下描述:
元素申明:元素类型
JavaFX中定义的元素必然具有以下三类特征之一:Optional、Required和Sequence。JavaFX变量所属允许的数据类型可以是JavaFX的基本数据类型,如String、Integer、Number、Boolean、 和Duration等,也可以是Java的类,如java.util.Map等。
1、Optional
属于Optional的数据类型一般是表示此元素的值可以有也可以没有。元素有值,则其值为被赋予的值;元素无值,则其值为null。它们一般被指定为某种类。有此类情况的是如下定义:
var k : Foo; //这里Foo是一个在JavaFX中定义的类(如何定义类,请看本书后部章节)。
在这个例子中k就是我们定义的变量,它的数据类型是Foo类。对于k来说它可以有个具体的数据类型为Foo的值,它也可以没有值,没有值时其为null。我们称类似变量k的变量为Optional变量,它可能有具体值,也可能为null。
2、Required
属于Required的数据类型一般表示此变量必需有一个具体值,而不能为null。它们一般都被指定为JavaFX的基本数据类型,如String、Integer、Number、Boolean、 和Duration等。如下列定义:
var k : Integer;
这个例子中k就是我们定义的变量,它的数据类型为JavaFX基本类型中的Integer类型。它必需是某个具体值,而不能给它赋予null。
3、Sequence
属于Sequence的数据类型可以是属于任何数据类型的数组,它可以是由若干个(包括0个)具有相同数据类型的元素组成的序列。如下例所示:
var seq : Integer[];
如果程序员只是定义了变量,但没有给出变量指定的初始化值,则JavaFX为定义的每个变量都设定一个默认值(不同类型默认值如下表)。
类型 |
描述 |
默认值 |
备注 |
String |
字符串类型 |
“” |
对应Java中java.lang.String |
Integer |
整型 |
0 |
对应Java中java.lang.Integer |
Number |
浮点类型 |
0.0 |
对应Java中java.lang.Double |
Boolean |
布尔类型 |
false |
对应Java中java.lang.Boolean |
Duration |
持续时间类型 |
0ms |
属于JavaFX中的javafx.lang.Duration |
Object |
对象类型 |
null |
对应Java中java.lang.Object |
其它定义的类名 |
其它定义的类 |
null |
|
function |
函数类型 |
null |
|
[] |
数组类型 |
|
|
|
|
|
|
在JavaFX中定义变量使用var或def关键词,看下面例子:
var name:String; //定义name变量,其类型为String,默认值为””
var age:Integer; //定义age变量,其类型为Integer,默认值为0;
var money:Number; //定义money变量,其类型为Number,默认值为0.0
var officer:Boolean; //定义officer变量,其类型为Boolean,默认值为false
我们可以在定义变量的同时给变量一个初始化值,如下:
var stature:Number=173.2 //定义stature变量,其类型为Number,初始值为173.2
var address:String=”南昌市南京东路”; //定义address变量,其类型为String,
//初始值为“南昌市南京东路”
我们也可以使用def来定义变量,如下:
def first_name:String=”李”; //定义first_name变量,其类型为String,初始值为”李”
def avoirdupois:Number=75 //定义avoirdupois变量,其类型为Number,初始值为75
使用var和def定义的变量稍微不同的是,使用var定义的变量在程序执行过程中可以对变量的值进行修改,而使用def定义的变量其值在程序运行过程中是不允许修改的。更确切的说使用def定义的应该是一个常量。通过def关键词定义变量名时,必需立即赋予初始化值。
下面详细介绍JavaFX内置的基本数据类型。
一、String类型
先从使用频率最高的String类型开始。在JavaFX代码中定义String类型的变量,并对其赋值可以参考如下格式:
var name:String=”赵文彬”;
或
var name:String=’赵文彬’;
大家可能注意到了,其实在JavaFX中给变量赋予字符串类型值可以有两种合法格式,使用双引号来定义字符串和采用单引号来定义字符串,他们的结果是等价的。有一点要注意,如果采用双引号来定义字符串,则必需以双引号开始,并以双引号结束;如果采用单引号来定义字符串,则必需以单引号开始,并以单引号结束,不能单引号、双引号混用。除此之外还要注意在字符串当中不能使用“{”、“}”和“\”字符,因为对于JavaFX来说“{”、“}”和“\”字符有着特殊作用。在JavaFX的字符串当中使用“{”和“}”表示的是其中被包含部分是一个合法的表达式,这部分内容会被表达式的返回内容替换,举例如下:
def first_name=”Zhao”;
var name=”Hello,{first_name}”;
println(name);
以上代码执行后,在控制台中打印出的字符串是“Hello,Zhao”,而不是“Hello,{first_name}”。这一点特别要注意,在字符串当中被“{”和“}”包括的部分将作为表达式被执行,表达式返回的值被替换于此处。
和Java语言相同,“\”是转义符号。如果要在字符串中保持有“\”,就需要如下面代码写法:
var addr=”Hello,\\liu”;
以上代码执行后获得的字符串就是“Hello,\liu”。
和Java代码不同,在JavaFX中对字符串的定义可以采用双引号定义,也可以采用单引号定义,因此当采用双引号定义字符串时,字符串内部存在双引号字符时必需对其通过转义符号进行转义,同理当采用单引号定义字符串时,字符串内部存在单引号字符时必需采用转义字符对其进行转义。
var addr=”JiangXi NanChang ‘333’ \”222\””;
var address=’JiangXi NanChang \’333\’ “222”’;
以上代码addr和address变量中最后得到的字符串是“JiangXi NanChang ‘333’ “222””。但仔细看这两者代码是有略微差异的。对于addr变量的初始化,因为其使用双引号定义字符串,因此对于字符串当中出现的双引号要进行转义,而对于其中的单引号却可以不予以处理。而对于address变量的初始化,因为其使用单引号定义字符串,因此对于字符串当中出现的单引号要进行转义,而对于其中的双引号却可以不予以处理。
在JavaFX语法中支持对字符串的定义分行进行。看下面的例子:
var greeting=”Hello ”
“Zhao”;
Println(greeting);
猜猜看最后输出什么?它的输出结果是“Hello Zhao”,也就是说其等价于如下定义:
var greeting=”Hello Zhao”;
我们再来改东一些代码:
var greeting=”Hello ”
‘Zhao’; //注意此处被改成以单引号定义的字符串了
Println(greeting);
猜猜看这又输出什么?对了,它的输出结果还是“Hello Zhao”,也就是说不管这两端代码是采用双引号或者单引号定义的字符串,只要它们彼此临近,这样它们就会被自动拼接到一起。好,我们再来改改代码:
var greeting=”Hello ”; //注意此处有了个分号,上面的代码在结尾处都没有分号
‘Zhao’;
Println(greeting);
猜猜看这会又输出什么?对了,现在输出不一样了,它输出“Hello ”,也就是说在这段代码后面加入分号后代表的是语句的结束,因此代码
var greeting=”Hello ”;
和代码
‘Zhao’;
现在是两段彼此独立的代码,此时不在适合临近原则了。
接下来,还要讲到一个是否有用的东西,就是本地化。在开发应用程序,特别是涉及到出口的面向全球的应用程序时,常常牵扯到不同国家的不同语言,因此支持本地化成为应用程序是否关键的一部分,它可以保障在不改动代码的情况下自动根据使用用户所在国家的语言自动显示适宜的界面文字提示。
在JavaFX语法中当遇到以##的符号时,JavaFX就会启动本地化服务,使用和用户所使用国籍语种相同的配置文件中的设置来替换默认的文字串。JavaFX的本地化配置文件采用如果格式:
1、采用和fx脚本文件名同名加语种代码形成以fxproperties为扩展名的文件。例如:
假设fx脚本文件的文件名为:Foo.fx,当前语种为法语(法语语种代号为fr),则其对应的本地化文件名就为Foo_fr.fxproperties。
2、fxproperties文件内部采用Key=Value格式进行本地化设置。例如:
"Monday" = "Lundi"
"ABOUT" = "JavaFX est un langage de script cool !"
fxproperties文件中的Key值部分就是出现在JavaFX脚本文件中以##开头的字符串。例如我们可以在fx脚本文件中如下写:
def mon = ##"Monday";
这样当此fx脚本执行时,就会根据用户所选择语种到相应的fxproperties文件中去查找Key值为“Monday”的内容,并用此内容替换原fx脚本中默认的内容。
还有一种写法:
def about = ##[ABOUT]"JavaFX is a cool scripting language!";
这里我们在##后面放置了用“[”和“]”字符括起来的字符串,它的作用也是起Key值的用途。因为此段文字很长,我们就给它取个别名(可以这样形象的理解)叫“ABOUT”,这样JavaFX执行到此段时,发现有##符号就知道要进行本地化服务,由发现后面有由“[”和“]”字符括起的文字,就知道这括起的文字就是在fxproperties文件中查找的Key值。
如果在JavaFX脚本中定义了##,但却没有找到相应语种的fxproperties文件,或者在fxproperties文件中没有找到相应的Key值,则JavaFX会使用原始的字符串作为输出。例如对于以上代码中about变量来说,假如在Foo_fr.fxproperties文件中没有找到ABOUT的Key值,则其变量中最终的值就是“JavaFX is a cool scripting language!”。假如在Foo_fr.fxproperties文件中找到ABOUT的Key值,则其变量中最终的值就是“JavaFX est un langage de script cool !”。
fxproperties文件默认采用的字符集是UTF-8,如果希望采用其它的字符集编码方式可以通过@charset来指定,如下:
@charset "ISO-8859-1";
在JavaFX中String默认值为””;
二、Integer类型
接下来讲解另一个高度使用的JavaFx数据类型——Integer类型。在JavaFX语言中Integer类型可以表达的数据域范围为-2147483648到2147483647。在JavaFX语法中我们可以以十进制、十六进制、八进制方式给变量赋值,如下:
var on
var two=0x10;
var three=010;
var four=0X10;
以上代码定义了四个变量并初始化了值,初始化on
由上我们看到了JavaFX语法在描述数值时的方法:
1、十进制数描述时,采用1、2、3、4、5、6、7、8、9、0这样的数字来描述,并且不能以0开头;
2、十六进制描述时,采用以0x或者0X开头,后续1、2、3、4、5、6、7、8、9、0、A、B、C、D、E、F、a、b、c、d、e、f这样的数字或字符来描述。
3、八进制描述时,采用以0开头,后续1、2、3、4、5、6、7、0这样的数字来描述。
在JavaFX中,Integer类型默认值为0;
三、Number类型
Number类型在JavaFX中用来表述浮点类型的值。其描述的方法如下:
符号部分+整数部分+.+小数部分+e(E)+指数部分
其中:
1、符号部分用于表述数字的正负,通过符号+或符号-来表示。
2、整数部分可以是1、2、3、4、5、6、7、8、9、0中的任意数字的组合,如果整数部分为0,也可以不书写整数部分的0,而直接标记小数点。
3、小数部分可以是1、2、3、4、5、6、7、8、9、0中的任意数字的组合。
4、如果数值采用指数形式表述,则可以在小数部分之后使用e或者E来表示,在e或者E之后的部分就是指数部分。
5、指数部分可以是1、2、3、4、5、6、7、8、9、0中的任意数字的组合,可以为任意的正负值,负值需要在指数部分首部加符号-,正值时可以忽略符号+,但指数部分不允许出现小数。
举例如下:
var k=12.3;
var h=0.123;
var j=.234;
var y=1.2e2;
var w=1.2e-4;
var r=1.1E-5;
四、Boolean类型
Boolean类型在JavaFX语法中只有true和false两个值,只能取其中之一。在这里要注意true和false的大小写。
五、Duration类型
Duration类型在JavaFX中表述一段持续时间。其表示格式:
数值部分+时间单位部分
其中:
1、数值部分可以是整型也可以是浮点型;
2、时间单位部分可以使用的值为:ms、s、m、h。ms代表的是毫秒,s代表的是秒,m代表的是分钟,h代表的是小时。
六、function类型
function类型在一定意义上你可以将其理解成一个函数指针类型,它的行为也确实就是函数指针这种概念。在定义function类型时可以为它指定参数、参数类型和返回类型,例如:
var myFunc:function(:String,:String):String;
在上面这段代码中定义了myFunc变量为function类型,且其具有两个字符串类型的参数,返回一个字符串类型值。
myFunc=function(firstName:String,secondName:String):String{
return “{firstName}{secondName}”;
}
以上这段代码就是对前面定义的myFunc变量进行赋值。myFunc变量被赋值后,我们就可以使用此变量,代码如下:
println(myFunc(‘zhao’,”wenbin”));
执行以上代码后输出结果就是“zhaowenbin”。
七、序列类型
JavaFX语言中有一种Sequence类型,它其实就如同Java中的数组类型,但在JavaFX中Sequence类型不是一个对象,这就是说Sequence嵌套时,系统会自动将嵌套的Sequence进行平展,使其变成单个序列。如:
def weekDays: String[] = ["Mon","Tue","Wed","Thu","Fri"];
def days = [weekDays, ["Sat","Sun"]];
其等效于:
def days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
通过上例我们可以知道,JavaFX中定义变量为序列类型时是在变量的数据类型后加上“[]”。定义序列值时是显式列出其各个项,每个项之间都用一个逗号进行分隔,最后将整个列表用方括号 "[" 和 "]" 括起来。当然也有一种简化的定义一个区间的方式,如下:
def nums = [1..100];
这样变量nums就定义了一个由1到100的整数组成的序列类型。
在定义Sequence类型时往往有更加复杂的区间值定义,这时就可以使用谓词(即一种布尔表达式),例如下面是我们开始定义的变量nums:
def nums = [1,2,3,4,5];
如果此时我们要定义另一个变量nums2,其中要求序列是在nums基础上值大于2的数值组成的序列,则我们可以定义如下:
def nums2 = nums[n | n > 2];
这时nums2中定义的值就是[3,4,5]。
在JavaFX中定义序列时还可以使用一种称为范围表达式的方式,如下所示:
var num = [1..10];
num变量中定义了1到10的序列,这时如果我们需要获得是1到10之间奇数的序列,我们就可以如下进行方便定义:
var num = [1..10 step 2];
这时num变量中的值就是1到10之间奇数组成的序列,也就是[ 1, 3, 5, 7, 9 ]。
在这里我们接触到了一个step的关键词,默认情况下step的值为1,因此这也是我们在定义[1..10]时,其值为1到10的整数序列的原因,我们可以通过改变step值来实现我们的一些特殊要求,step的值可以为正值,也可以为负值。例如要创建降序范围,请确保第二个值小于第一个值,并指定一个负的 step 值:
var nums = [10..1 step -1];
其得到的序列值为[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]。
如果在创建降序序列时没有提供负的 step 值,则会生成一个空序列。或者如果在创建降序序列时,起始值小于终止值,即使指定了负的step值,也会获得一个空序列。
在指定范围时,我们还可以通过以下表达式来确定序列范围:
def nums = [1..<100];
上述表达式获得的序列是1到100的整数序列,但不包括100。
假设我们在定义序列时,其中某项指定为null,情况会如何?
var addr=[45, null, 9, [], 88, [13]];
JavaFX将自动将序列中的null转换成空序列来对待,因此addr中的实际序列值为[ 45, 9, 88, 13 ]。也就是null和空序列不产生任何作用,完全可以忽略其存在。下面先讲讲如何获取序列中的某具体项值,然后我们回过头来继续讨论这个问题。
我们要获取序列中某个具体项的值可以采用如下方式:
println(addr[2]);
上面代码中我们是用来或其addr序列中第三项的值,在代码中被中括号括起的数值2表示的就是序列第三项的序号,因为序列的序号是从0开始的,因此第三项的序号就是2。这种获取数据的方式完全类似于Java中对数值的访问。
好,我们学会了如何获取序列中具体项的值的方法,那么对于上述代码最后获得什么结果?是等于9?我们回顾一下前面的介绍——“JavaFX完全忽略null和空序列”,因此实际我们得到的序列应该是[ 45, 9, 88, 13 ]。因此第三项值应该是88,而不是9。理解这点很重要。
序列在JavaFX中有很重要的作用,后面在讲到其它语法时还会涉及到序列的知识,现在我们将序列的讲解暂且搁置下来,我们继续新的知识学习。
八、Void类型
Void类型只是用来表示表达式没有值。因为在JavaFX中,一切都是表达式,因此对于如下代码:
var aa=function (){var i=2;var k=3;i==k;}
var bb=aa();
执行以上代码变量bb的值为false。这里我们看到函数并没有写返回语句,但在JavaFX中最后一条语句的值就是函数的返回值,显然当i为2,k为3时,i==k表达式返回的为false,而i==k又是函数的最后一条执行语句,因此此函数返回值就为false。
而通过将函数返回值声明为Void,就可以使函数不具备返回值。如下:
function gab(val : Number) : Void {
var remains = val;
while (remains > 1.0) {
println( remains );
remains = remains / 2;
}
}
以上函数gab的执行就没有返回值,因为定义时已经声明函数返回类型为Void类型,因此此函数没有返回值。
这里需要特别注意:JavaFX 关键字 Void 以大写字母 V 开头,这和Java中的void类型是不同的。
如果我们前一个例子稍做修改看看结果如何?
var aa=function ():Void{var i=2;var k=3;i==k;}
var bb=aa();
如果大家在跟着动手敲代码的话就会发现,此代码和上面稍有变化的地方在于增加了代码中用黑体加粗的部份,这样一来第二行代码就不正确了(如果使用Netbeans IDE开发工具编写JavaFX脚本的话,开发工具就会提示此行的错误),因为我们上面已经定义了aa返回类型为Void,也就是没有返回值的,因此bb变量是不能接收到aa函数的返回值的,这段代码写法都不正确。