JavaFX文档(6)开始JavaFX之旅——4 使用FXML来创建用户界面

时间:2021-02-01 17:04:11

声明:如需转载,请注明出处:http://blog.csdn.net/originer

原文地址:http://docs.oracle.com/javase/8/javafx/get-started-tutorial/fxml_tutorial.htm


本文介绍JavaFX FXML的特点,FXML基于XML,用于将用户界面与程序逻辑分离。这里将介绍用FXML来创建与前面的例子一样界面。

创建工程

在IDE中创建工程,在其中创建如下三个文件:
  • FXMLExample.fxml
  • FXMLExample.java
  • FXMLExampleController.java

加载FXML源文件 

编写FXMLExample.java文件,与之前教程讲的的一样编写main方法、定义stage和scene,并使用FXMLLoader来加载FXML源文件、返回其代表的图形界面元素(译者注:目前代码还无法运行,因为FXML内容为空会在运行时报错):

@Override
    public void start(Stage stage) throws Exception {
       Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));
    
        Scene scene = new Scene(root, 300, 275);
    
        stage.setTitle("FXML Welcome");
        stage.setScene(scene);
        stage.show();
    }

建议设置scene的宽和高,在本例中是300和275,如果没有设置则scene默认会使用能显示其内容的最小面积。

修改导入元素

修改FXML文件,首先需要做的事情是按照下面的代码修改import元素:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

如你所见,FXMAL与java类似,对需要的引用的类使用import来进行设置。

创建GridPane布局

如果你使用的是NetBeans来编程,注意生成的FXML文件默认采用了AnchorPane,而我们在这里需要使用GridPane,因此需要修改FXML移除AnchorPane并按下面的样例进行修改:

<GridPane fx:controller="fxmlexample.FXMLExampleController" 
    xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<padding><Insets top="25" right="25" bottom="10" left="25"/></padding>

</GridPane><span style="font-weight: normal;">
</span>

(译者注:目前代码还无法运行,因为在fx:controller属性所指定的FXMLExampleController类尚未编写完毕,如果你希望此时看到运行效果,将fx:controller="fxmlexample.FXMLExampleController"删除即可运行查看
在本例中,GridPanle layout是FXML的根元素,它有两个属性。fx:controller属性用于指定事件处理控制器。xmlns:fx属性是必须的,用于指定fx命名空间。

alignment属性将grid中默认的靠左上对齐改为居中对齐。gap属性用于管理行和列之间的间隔,padding属性管理gird pane外围的空间大小。

当窗口被改变大小时,在grid pane中的节点将会根据其布局约束来自动调整大小。在本例中所有的控件均将居中显示。padding属性将确保在窗口被缩小时,使得gird pane周围仍能留有间隔。

增加文本和密码域

按照下面的样例,在<GridPane>元素中添加代码:

<Text text="Welcome" 
        GridPane.columnIndex="0" GridPane.rowIndex="0"
        GridPane.columnSpan="2"/>
 
    <Label text="User Name:"
        GridPane.columnIndex="0" GridPane.rowIndex="1"/>
 
    <TextField 
        GridPane.columnIndex="1" GridPane.rowIndex="1"/>
 
    <Label text="Password:"
        GridPane.columnIndex="0" GridPane.rowIndex="2"/>
 
    <PasswordField fx:id="passwordField" 
        GridPane.columnIndex="1" GridPane.rowIndex="2"/><span style="font-weight: normal;">
</span>

第一行创建了一个Text对象,其文本值为Welcome,GridPane.columnIndexGridPane.rowIndex属性指定Text控件在网格中的位置。注意在网格布局中表示行和列的值均从0开始,将Text控件放到(0,0)表示将其放到第1列、第1行。GridPane.columnSpan设置为2表示Welcome文字框将在网格中横跨两列。由于后面的教程将使用样式表将Text的字体放大到32point,因此需要预留出空间。

第二行在网格的第0列,第1行创建了一个Label对象,其文字内容为“User Name”,在第1列第1行创建了TextField对象;类似地在后续创建了Label和对应的PasswordField对象。

与之前使用代码创建界面的例子类似,使用FXML也可以通过设置gridLinesVisible属性为true来显示网格线以便于调试。如果将<gridLinesVisible>true</gridLinesVisible>放到<padding></padding>之后即可实现。运行效果如图(译者注:注意需暂时删除fx:controller="fxmlexample.FXMLExampleController"):

JavaFX文档(6)开始JavaFX之旅——4 使用FXML来创建用户界面


增加按钮和文本

最后需添加的两个控件:用于提交数据的Button控件、当用户点击按钮时用于显示信息的Text控件。在</GridPane>之前添加下面的样例代码:

<span style="font-weight: normal;"><HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4">
        <Button text="Sign In"  onAction="#handleSubmitButtonAction"/>
</HBox>
<Text fx:id="actiontarget" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" GridPane.rowIndex="6"/>
</span>

HBox被放到网格中的第1列、第4行,它设置了与GridPane 布局中的其它控件不同的对齐方式,alignment设置为了botton_right,这样会将其中的节点靠右下角对齐。

HBox下有一个子节点:一个带有"Sign In"文字说明的按钮,其onAction 属性被设置为handleSubmitButtonAction()。FXML提供了快速定义用户界面的方式,但是并没有提供实现应用程序行为的方式。handleSubmitButtonAction()方法所表示的实际行为动作将会在下一节来介绍。

其中的Text控件设置了fx:id属性,它会在当前FXML文档的命名空间中创建一个对应的变量,可以通过它来应用对应的控件。

增加处理事件代码

FXMLExampleController.java代码按照下面来修改:

package fxmlexample;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
 
public class FXMLExampleController {
    @FXML private Text actiontarget;
    
    @FXML protected void handleSubmitButtonAction(ActionEvent event) {
        actiontarget.setText("Sign in button pressed");
    }

}<span style="font-weight: normal;">
</span>

其中的@FXML注解被用在了private成员变量和protected事件处理方法上,显然它与FXML文件中的fx:id="actiontarget"所表示的Text控件,以及onAction="#handleSubmitButtonAction"所表示的事件动作对应。程序运行效果如下:

JavaFX文档(6)开始JavaFX之旅——4 使用FXML来创建用户界面


使用脚本语言来处理事件

除了使用Java代码来创建事件处理器之外,还可以通过脚本语言来创,这些脚本语言应提供与JSR 223相兼容的引擎,例如JavaScript、Groovy、Jython、Clojure。

下面来尝试使用JavaScript:

1.在FXML文件中(译者注:建议复制一份原FXML文件并重命名,并在java代码中重新关联,如何进行你懂的),在XML doctype声明之后添加JavaScript声明。

2.在Button元素中,修改对应的属性值:

<span style="font-weight: normal;">onAction="handleSubmitButtonAction(event);"
</span>


3.移除fx:controller属性,并在GridPane元素下添加<script>标签:

<GridPane xmlns:fx="http://javafx.com/fxml" 
              alignment="center" hgap="10" vgap="10">
         <fx:script>
             function handleSubmitButtonAction() {
                 actiontarget.setText("Calling the JavaScript");
             }
         </fx:script>

另外,也可以将JavaScript代码放到外部文件中(例如fxml_example.js),并且使用如下方式将其inculde进来:

<fx:script source="fxml_example.js"/>

下面是运行效果:

JavaFX文档(6)开始JavaFX之旅——4 使用FXML来创建用户界面

注意如果在FXML中使用脚本语言,IDE有可能不支持在调试时进行脚本代码的debug。


使用CSS
最后一个任务是使用CSS来改变应用的外观。
上一课中用到的Login.css和background.jpg复制过来,然后在FXML中GridPane结束标记前引用它们:
<stylesheets>
    <URL value="@Login.css" />
  </stylesheets>

</GridPane>
译者注:实际上这样仅仅能修改背景和按钮的样式,Welcome文本的样式并无法修改,你需要在FXML中为其添加fx:id属性,而且上之前的例子中给出了一个错误的引导,如果使用“welcome-text”作为其id,程序将无法运行通过,将会得到 javafx.fxml.LoadException: Invalid identifier异常;因此你需要将id修改为"welcometext",并在对应的Login.css文件中,将#welcome-text修改为#welcometext
最后你的代码应该大致如此:
1.FXMLExample.java
package fxmlexample;


import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class FXMLExample extends Application{

	@Override
	public void start(Stage stage) throws Exception {
		Parent root = FXMLLoader.load(getClass().getResource("FXMLExample.fxml"));
//		Parent root = FXMLLoader.load(getClass().getResource("FXMLExample_JS.fxml"));
//		Parent root = FXMLLoader.load(getClass().getResource("FXMLExample_JS2.fxml"));
        Scene scene = new Scene(root, 300, 275);
    
        stage.setTitle("FXML Welcome");
        stage.setScene(scene);
        stage.show();
	}
	
	public static void main(String[] args) {
		launch(args);
	}
	
}
2.FXMLExampleController.java
package fxmlexample;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
 
public class FXMLExampleController {
    @FXML private Text actiontarget;
    @FXML protected void handleSubmitButtonAction(ActionEvent event) {
        actiontarget.setText("Sign in button pressed");
    }

}
3.FXMLExample.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<GridPane fx:controller="fxmlexample.FXMLExampleController" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
	<padding><Insets top="25" right="25" bottom="10" left="25"/></padding>
	<gridLinesVisible>true</gridLinesVisible>
	<Text fx:id="welcome-text" text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
 
    <Label text="User Name:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
 
    <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/>
 
    <Label text="Password:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
 
    <PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
	
	<HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4">
        <Button text="Sign In"  onAction="#handleSubmitButtonAction"/>
	</HBox>
	<Text fx:id="actiontarget" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" GridPane.rowIndex="6"/>
	
	<stylesheets>
    	<URL value="@Login.css" />
  	</stylesheets>

</GridPane>

4.Login.css
.root {
     -fx-background-image: url("background.jpg");/* 设置背景图 */
}
.label {
	/* 设置字体 */
    -fx-font-size: 12px;
    -fx-font-weight: bold;
    -fx-text-fill: #333333;
    /* 设置阴影 */
    -fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}
#welcometext {
	/* 增加字号、改变字体,将字体颜色变成深灰,增加内部阴影特效,使其呈现浮雕效果 */
   -fx-font-size: 32px;
   -fx-font-family: "Arial Black";
   -fx-fill: #818181;
   -fx-effect: innershadow( three-pass-box , rgba(0,0,0,0.7) , 6, 0.0 , 0 , 2 );
}
#actiontarget {
  -fx-fill: FIREBRICK;
  -fx-font-weight: bold;
  -fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );  
}
.button {
    -fx-text-fill: white;
    -fx-font-family: "Arial Narrow";
    -fx-font-weight: bold;
    -fx-background-color: linear-gradient(#61a2b1, #2A5058);/*创建线性渐变*/
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
}
.button:hover {
    -fx-background-color: linear-gradient(#2A5058, #61a2b1);
}