FXML作为XML-based,UI构造器。其相关的规则值得我们去理解。
FXML元素分类:
- A class instance
- A property of a class instance
- A "static" property
- A "define" block
- A block of script code
FXML应该在根元素定义prefix : xmlns:fx=http://javafx.com/xml
Class instance 元素
实例声明
<?import javafx.scene.control.Label?> <Label text="Hello, World!"/>
Maps
<HashMap foo="123" bar="456"/>
fx:value-对于没有默认构造器的类如String,Double等但是有valueOf方法的
<String fx:value="Hello, World!"/> <Double fx:value="1.0"/> <Boolean fx:value="false"/>
fx:factory-对于使用静态工厂方法的
<FXCollections fx:factory="observableArrayList"> <String fx:value="A"/> <String fx:value="B"/> <String fx:value="C"/> </FXCollections>
Builders使用构造器模式的类:如Color
<Color red="1.0" green="0.0" blue="0.0"/>
<Color> <red>1.0</red> <green>0.0</green> <blue>0.0</blue> </Color>
fx:include-包含另一个fxml文件或者国际化资源文件resource bundle
<fx:include source="filename"/>
<?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns:fx="http://javafx.com/fxml"> <children> <fx:include source="my_button.fxml"/> </children> </VBox>
my_button.fxml
<?import javafx.scene.control.*?> <Button text="My Button"/>
包含国际化资源文件
<fx:include source="filename" resources="resource_file" charset="utf-8"/>
fx:constant
<Button> <minHeight><Double fx:constant="NEGATIVE_INFINITY"/></minHeight> </Button>
fx:reference-通过fx:id进行引用
<ImageView> <image> <fx:reference source="myImage"/> //用于替代ImageView的image属性 </image> </ImageView>
<ArrayList> <fx:reference source="element1"/> <fx:reference source="element2"/> <fx:reference source="element3"/> </ArrayList>
fx:copy-暂时别用,以后也许会改变
fx:root-指向root元素
Property元素
property元素支持强制类型转换。
分为:
- A property setter
- A read-only list property
- A read-only map property
Property Setters
<?import javafx.scene.control.Label?> <Label> <text>Hello, World!</text> </Label>
<?import javafx.scene.control.Label?> <Label text="Hello, World!"/>
ReadOnly List Property
<Group xmlns:fx="http://javafx.com/fxml"> <children> <Rectangle fx:id="rectangle" x="10" y="10" width="320" height="240" fill="#ff0000"/> ... </children> </Group>
ReadOnly Map Property
<?import javafx.scene.control.*?> <Button> <properties foo="123" bar="456"/> </Button>
Default Property
<?import javafx.scene.*?> <?import javafx.scene.shape.*?> <VBox xmlns:fx="http://javafx.com/fxml"> <Button text="Click Me!"/> ... </VBox>
Static Property
<GridPane> <children> <Label text="My Label"> <GridPane.rowIndex>0</GridPane.rowIndex> <GridPane.columnIndex>0</GridPane.columnIndex> </Label> </children> </TabPane>
定义Blocks----fx:define
fx:define定义的内容不会被添加到Scene Graph,最典型的应用就是单选按钮组中ToggleGroup的fx:define
引用它之前需要添加符号$
<VBox> <fx:define> <ToggleGroup fx:id="myToggleGroup"/> </fx:define> <children> <RadioButton text="A" toggleGroup="$myToggleGroup"/> <RadioButton text="B" toggleGroup="$myToggleGroup"/> <RadioButton text="C" toggleGroup="$myToggleGroup"/> </children> </VBox>
Attributes:
分类:
- A property of a class instance
- A "static" property
- An event handler
Property Attribute与Property Element是有区别:
1.property attribute只有当元素关闭时才会生效
2.property attribute还支持解析操作(resolution operators):
- Location resolution位置解析
- Resource resolution国际化资源解析
- Variable resolution变量解析
Location resolution
@代表与当前fxml文件在同一目录
<ImageView> <image> <Image url="@my_image.png"/> </image> </ImageView>
注意@路径解析后面接的必须是已经被URL编码的字符。如My Image.jpg应该写成这样
<Image url="@My%20Image.png"/>
Resource resolution
%代表该字符变量应该用国际化资源解析
<Label text="%myText"/>
Variable resolution
$代表需要进行变量解析,一般与fx:define配合使用
<fx:define> <ToggleGroup fx:id="myToggleGroup"/> </fx:define> ... <RadioButton text="A" toggleGroup="$myToggleGroup"/> <RadioButton text="B" toggleGroup="$myToggleGroup"/>
转义处理:
<Label text="\$10.00"/>
表达式绑定:
${expr}
<TextField fx:id="textField"/> <Label text="${textField.text}"/>
支持的其他操作:
"string"
‘string‘ |
A string constant |
true
false |
A boolean constant |
null | A constant representing the null value |
50.0
3e5 42 |
A numerical constant |
-
(unary operator) |
Unary minus operator, applied on a number |
!
(unary operator) |
Unary negation of a boolean |
+ -
* / % |
Numerical binary operators |
&& || | Boolean binary operators |
> >=
< <= == != |
Binary operators of comparison.
Both arguments must be of type Comparable |
Static Properties与Instance Properties类似
static properties attribute与element有点区别:
<Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
EventHandlers
适用于setOnEvent类的方法(如setOnAction)
脚本化处理方式:
使用javascript声明与脚本
<?language javascript?> ... <VBox> <children> <Button text="Click Me!" onAction="java.lang.System.out.println(‘You clicked me!‘);"/> </children> </VBox>
控制器类Controller方法处理方式:
注意#号,与@FXML注解
<VBox fx:controller="com.foo.MyController" xmlns:fx="http://javafx.com/fxml"> <children> <Button text="Click Me!" onAction="#handleButtonAction"/> </children> </VBox>
public class MyController { @FXML public void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); } }
下面这种方式也是有效的
public class MyController { public void handleButtonAction() { System.out.println("You clicked me!"); } }
对于Collections与properties的特殊的处理
ObservableList, ObservableMap orObservableSet uses
a special onChange attribute that points
to a handler method with aListChangeListner.Change, MapChangeListener.Change or SetChangeListener.Changeparameter
respectively.
<VBox fx:controller="com.foo.MyController" xmlns:fx="http://javafx.com/fxml"> <children onChange="#handleChildrenChange"/> </VBox>
public class MyController { public void handleChildrenChange(ListChangeListener.Change c) { System.out.println("Children changed!"); } }
对于parent property的处理:
public class MyController { public void handleParentChange(ObservableValue value, Parent oldValue, Parent newValue) { System.out.println("Parent changed!"); } }
<VBox fx:controller="com.foo.MyController" xmlns:fx="http://javafx.com/fxml" onParentChange="#handleParentChange"/>
Scripting脚本化
<?language javascript?>
<fx:script>
<?language javascript?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns:fx="http://javafx.com/fxml"> <fx:script> function handleButtonAction(event) { java.lang.System.out.println(‘You clicked me!‘); } </fx:script> <children> <Button text="Click Me!" onAction="handleButtonAction(event);"/> </children> </VBox>
从外部文件读取脚本。
<?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns:fx="http://javafx.com/fxml"> <fx:script source="example.js" charset="cp1252"/> <children> <Button text="Click Me!" onAction="handleButtonAction(event);"/> </children> </VBox>
example.js:
function handleButtonAction(event) { java.lang.System.out.println(‘You clicked me!‘); }
<fx:script> var myText = "This is the text of my label."; </fx:script> ... <Label text="$myText"/>
Controllers
fx:controller
<VBox fx:controller="com.foo.MyController" xmlns:fx="http://javafx.com/fxml"> <children> <Button text="Click Me!" onAction="#handleButtonAction"/> </children> </VBox>
public class MyController { public void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); } }
当然Controllers类还可以实现Initializable接口,以便在被加载时可以调用初始化方法initialize()
<VBox fx:controller="com.foo.MyController" xmlns:fx="http://javafx.com/fxml"> <children> <Button fx:id="button" text="Click Me!"/> </children> </VBox>
package com.foo; public class MyController implements Initializable { public Button button; @Override public void initialize(URL location, Resources resources) button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("You clicked me!"); }
前面介绍的都是通过public修饰field与方法,但是这破坏了封装性原则。
由于controller是对FXML Loader可见的,所以没必要对外部开放访问权限。
这样我们就必须通过@FXML注解进行修饰。
public class MyController { @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); } }
public class MyController implements Initializable { @FXML private Button button; @FXML protected void initialize() button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("You clicked me!"); }
Nested Controllers嵌套的控制器访问。
FXMLLoader
URL location = getClass().getResource("example.fxml"); ResourceBundle resources = ResourceBundle.getBundle("com.foo.example"); FXMLLoader fxmlLoader = new FXMLLoader(location, resources); Pane root = (Pane)fxmlLoader.load(); MyController controller = (MyController)fxmlLoader.getController();
使用FXML结合FXMLLoader自定义UI组件
通过使用<fx:root>元素指定root元素的类型:
<?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml"> <TextField fx:id="textField"/> <Button text="Click Me" onAction="#doSomething"/> </fx:root>
自定义的UI组件:
package fxml; import java.io.IOException; import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.TextField; import javafx.scene.layout.VBox; public class CustomControl extends VBox { @FXML private TextField textField; public CustomControl() { FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("custom_control.fxml")); fxmlLoader.setRoot(this); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } } public String getText() { return textProperty().get(); } public void setText(String value) { textProperty().set(value); } public StringProperty textProperty() { return textField.textProperty(); } @FXML protected void doSomething() { System.out.println("The button was clicked!"); } }
使用自定义的UI组件:
HBox hbox = new HBox(); CustomControl customControl = new CustomControl(); customControl.setText("Hello World!"); hbox.getChildren().add(customControl);
<HBox> <CustomControl text="Hello World!"/> </HBox>