JavaFX

JavaFX 是 Java 的一个实现客户端窗口的框架,该框架运行用户将 UI 设计和代码逻辑分开来,还提供了 SceneBuilder 可视化拖动绘制 UI 并自动生成 Controller 骨架结构。

Controller

controller 用于处理页面的标记和业务逻辑处理,可以在里面写一些 handler 、设置监听等。controller 通过 fxml 文件的 fx:controller 属性设置。

处理事件的 Handler

在 fxml 文件中定义的节点,声明的点击事件就在 controller 中给出对应的实现方法,示例如下

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox>

其中 button 的点击事件声明的 handleButtonAction 实现如下

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

Initialize() 方法

很多情况下,这有助于简化在样式中的声明事件处理方法。当 controller 中有更多的行为控制和需要操作的元素时,可以在 controller 中定义 initialize() 方法,这个会在与其相关联的 fxml 文件完全加载完成之后调用一次。

实现这个方法可以对内容进行各种必要的异步处理。这同样使得 controller 可以访问用于加载资源的文件和解析相对路径在文档中的位置(通常等同于本文档的位置)。

如例,下面的代码定义了 initialize 方法,其中实现了一个button 的 action 事件处理方法,而不是像之前的例子一样是通过事件处理属性定义的。这个 button 实例变量是在文件被加载时被 loader 注入的。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
      <!-- 没有属性 -->
        <Button fx:id="button" text="Click Me!"/>
    </children>
</VBox>
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!");
            }
        });
    }
}

FXML 注解

请注意,在之前的例子中,controller 中的成员变量和方法都是被声明为 public 的所以它们能够被 loader 调用。在实践中,这通常不会有什么问题,因为通常一个 controller 只会对创建它的 FXML loader 可见。但是如果你是对controller字段和 handler 方法可见性有着严格要求的程序员,可以考虑使用 @FXML注解。这个注解可用于标注允许 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!");
            }
        });
    }
}

嵌套 Controller

使用嵌套 Controller 的 FXML 文件通过 fx:include 属性可以直接定位到其中的成员 controller,这允许开发者很容易的访问定义在 include 中节点的 controller 中的方法。例如:

<VBox fx:controller="com.foo.MainController">
   <fx:define>
      <fx:include fx:id="dialog" source="dialog.fxml"/>
   </fx:define>
   ...
</VBox>

外部 controller 中使用内部 include 节点的 controller

public class MainController extends Controller {
    //include 的根节点
    @FXML private Window dialog;
    //嵌套的 controller 作为成员变量
    @FXML private DialogController dialogController;

    ...
}

当 initialize 方法被调用时,dialog 字段就会持有 dialog.fxml 的根节点,dialogController 就会持有内部节点的 controller。这样 MainController 就可以调用内部 controller 中的方法来发布和展示出 dialog 界面了。请注意,直接使用 fx:include 引入文件中内容的话,它将成为 main_window_content 布局的一部分,所以必须使用 fx:define 将 fx:include 包裹起来,将这两个窗口的布局分隔开。

FXMLLoader

FXMLLoader 类实际负责加载 fxml 源文件并返回结果对象图。比如,下面段代码从相对于加载类的类路径上加载了一个 fxml 文件并使用名为“com.example.foo”的资源包将其本地化。这个根节点的类型是javafx.scene.layout.Pane的子类,并且这个文件中定义了一个 MyController 类型的控制器。

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();

注意,load 方法返回的在文件中实际命名类组成的层次结构实例,而不是org.w3c.dom 节点代表的那些类。FxmlLoader 内部使用 javax.xml.streamAPI(也可被用于处理 xml 或者 stax) 来加载 fxml 文件。StAX 是一个极其高效的基于事件处理的 xml 解析 API ,概念上和它的 w3c 前身 SAX 类似。它可以一次性解析完 fxml 文件,而不是先加载一个 DOM 中间结构后,再去异步的处理。

自定义组件

FXMLLoader 中的 setRoot() 和 setController() 方法允许调用方将文件的root和controller 分别注入到文件的命名空间中,而不用委托 Loader 自己来完成这些值的创建。这使得开发者可以创建通过内部 xml 语言实现的可重用控件,但是从 API 的角度看起来和编程实现的控件一样。

比如,下下面这段 xml 就定义了一个简单的自定义控件结构,包含一个 TextField 和一个 Button 实例。其根容器被声明为一个 VBox 实例。

<?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>

fx:root 标签会创建一个已经定义过的根元素的引用,这个元素的值是通过调用 Fxml 中的 getRoot() 方法获取的。在调用 load() 之前,调用方必须先通过调用 setRoot() 设置过 root 的值。调用者同样也会通过调用 setController() 为文件的 controller 设置值,并将在文件被加载时作为该文件的 controller 使用。在创建自定 FXML 组件时,这两个方法通常会被放在一起使用。

一下这个示例,CustomControl 继承自 VBox ,并在它的构造函数中设置了 FXML 文件的 root 和 controller 。当这个类被加载时,CustomControl 中的内容将会根据之前的 FXML 文件中的内容进行填充(CustomControl 将会根据之前定义的 FXML 文件中的内容进行加载)。

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!");
    }
}

现在,我们就可以在代码或者 fxml 文件中使用这个自定义组件的示例了,就像使用其他的组件一样。

HBox hbox=new HBox();
CustomControl custom=new CustomControl();
custom.setText("hello");
hbox.getChildren().add(custom);

Fxml中使用

<HBox>
    <CustomControl text="Hello World!"/>
</HBox>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值