用 FXML 替换 JavaFX 中基于逻辑的 GUI

Replacing logic based GUIs in JavaFX with FXML

我正在使用以下代码在 JavaFX 中创建 GridPaneTextFields:

GridPane grid = new GridPane();
for (int i = 0; i < 9; i++) 
    for (int j = 0; j < 9; j++) {
        grid.add(createTextField(), j, i);
    }
}

createTextField() 是我使 returns 具有我设置的某些属性的 TextField 的方法。

我想开始使用 FXML 文件在 JavaFX 中创建我的 GUI,但我不太明白如何用 XML 文件。我将如何使用 FXML 重新创建上述代码? FXML 是否有 for 循环?

FXML 格式不提供循环方式。

FXML 文件应仅描述 UI 的静态(即非动态)、非重复部分。仅使用一个 FXML 文件不可能使 UI 完全动态,并且声明,例如,81 个不同的 TextField 很快就会变得难以管理。 UI 的这些部分应该由控制器处理,它允许您将 Java 代码连接到 FXML 文件。在您的情况下,这些字段可能应该添加到控制器的 initialize 方法中。例如:

FXML:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>

<GridPane fx:id="grid" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
          hgap="3" vgap="3" alignment="CENTER" fx:controller="com.example.Controller">
  <padding>
    <Insets topLeftBottomRight="5"/>
  </padding>
</GridPane>

控制器:

package com.example;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;

public class Controller {

  @FXML private GridPane grid;
  private TextField[][] fields;

  @FXML
  private void initialize() {
    grid.addEventFilter(KeyEvent.KEY_PRESSED, this::handleArrowNavigation);

    fields = new TextField[9][9];
    for (int i = 0; i < fields.length; i++) {
      for (int j = 0; j < fields[0].length; j++) {
        fields[i][j] = createTextField();
        grid.add(fields[i][j], j, i);
      }
    }
  }

  private TextField createTextField() {
    // ...
  }

  private void handleArrowNavigation(KeyEvent event) {
    // ...
  }
}

因为这似乎与 有关,我还展示了如何添加事件过滤器,因为在 FMXL 文件中无法这样做(只有 onXXX 事件处理程序属性可以被设置)。

在此示例中,使用 FXML 可能不值得,因为实际上您仅使用 FXML 来声明 GridPane。但这显然取决于应用程序其余部分的设计方式。

正如评论和其他答案中所述,没有办法在 FXML 中模拟循环,这种功能应该用 Java 而不是 FXML 编写。

请注意,一种模式是创建自定义 class 来处理您需要在代码中编写的部分。我通常不喜欢 subclassing layout classes 仅仅是为了向它们添加控件,尽管在这种情况下这至少增加了一些功能,并且可以很好地使用 FXML。

TextFieldGrid.java:

package org.jamesd.examples.textfieldgrid;

import javafx.beans.NamedArg;
import javafx.beans.property.StringProperty;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;

public class TextFieldGrid extends GridPane {

    private final StringProperty[][] text ;
    
    
    public TextFieldGrid(
            @NamedArg("numberOfColumns") int numberOfColumns,
            @NamedArg("numberOfRows") int numberOfRows 
    ) {
        
        text = new StringProperty[numberOfColumns][numberOfRows];
        for (int i = 0 ; i < numberOfRows ; i++) {
            for (int j = 0 ; j < numberOfColumns ; j++) {
                TextField textField = new TextField();
                add(textField, j, i);
                text[j][i] = textField.textProperty();
            }
        }
    }
    
    public StringProperty textProperty(int column, int row) {
        return text[column][row];
    }
    
    public final String getText(int column, int row) {
        return textProperty(column, row).get();
    }
    
    public final void setText(int column, int row, String text) {
        textProperty(column, row).set(text);
    }
    
    public int getNumberOfColumns() {
        return text.length;
    }
    
    public int getNumberOfRows() {
        return text[0].length;
    }
}

您可以使用哪个,例如在 FXML 文件中:

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

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>

<?import org.jamesd.examples.textfieldgrid.TextFieldGrid?>

<BorderPane xmlns="http://javafx.com/javafx/8.0.171"
    xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="org.jamesd.examples.textfieldgrid.Controller">
    <top>
        <Label text="Enter 81 values below:" />
    </top>

    <center>
        <TextFieldGrid fx:id="textFieldGrid" numberOfColumns="9" numberOfRows="9" />
    </center>

    <bottom>
        <Button text="Check values" onAction="#checkValues" />
    </bottom>

</BorderPane>

并在控制器中以正常方式引用功能:

package org.jamesd.examples.textfieldgrid;

import javafx.fxml.FXML;

public class Controller {

    @FXML
    private TextFieldGrid textFieldGrid ;
    
    @FXML
    private void checkValues() {
        for (int i = 0 ; i < textFieldGrid.getNumberOfColumns(); i++) {
            for (int j = 0; j < textFieldGrid.getNumberOfRows(); j++) {
                System.out.printf("[%d, %d]: %s%n", i, j, textFieldGrid.getText(i, j));
            }
        }
    }

}

为了完整起见,应用程序 class:

package org.jamesd.examples.textfieldgrid;

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

import java.io.IOException;

public class App extends Application {


    @Override
    public void start(Stage stage) throws IOException {
        Scene scene = new Scene(FXMLLoader.load(getClass().getResource("TextFieldGrid.fxml")));
        stage.setScene(scene);
        stage.show();
    }


    public static void main(String[] args) {
        launch();
    }

}