【问题标题】:Replacing logic based GUIs in JavaFX with FXML用 FXML 替换 JavaFX 中基于逻辑的 GUI
【发布时间】:2020-11-03 23:17:53
【问题描述】:

我正在使用以下代码在 JavaFX 中创建 TextFields 的 GridPane

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

createTextField() 是我创建的一个方法,它返回带有我设置的某些属性的 TextField

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

【问题讨论】:

  • 没有办法在 FXML 中做同样的事情。这种 UI 比 FXML 更适合 Java 代码。
  • 不,你不能。例如,您可以使用 FXML 或使用 SceneBuilder 创建网格。如果您仍想动态地执行此操作,因为它并不总是 9x9 网格,那么您可以在 FXML 中创建一个空网格,然后像现在一样添加网格单元。唯一的区别是您可以根据需要构建它(使用 SceneBuilder),并且您的控制器将包含 @FXML GridPane myGrid 或您在 SceneBuilder 中选择的任何其他名称。

标签: java xml user-interface javafx fxml


【解决方案1】:

FXML 格式不提供循环方式。

FXML 文件应仅描述 UI 的静态(即非动态)、非重复部分。仅使用 FXML 文件并声明 81 个不同的 TextFields 是不可能使 UI 完全动态化的,很快就会变得难以管理。 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) {
    // ...
  }
}

由于这似乎与your previous question 相关联,我还展示了如何添加事件过滤器,因为在 FMXL 文件中无法这样做(只能设置 onXXX 事件处理程序属性)。

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

【讨论】:

    【解决方案2】:

    正如 cmets 和其他答案中所述,无法模拟 FXML 中的循环,这种功能应该用 Java 而不是 FXML 编写。

    请注意,一种模式是创建一个自定义类来处理您需要在代码中编写的部分。我通常不喜欢仅仅为了向它们添加控件而对布局类进行子类化,尽管在这种情况下,这至少会添加一些功能,并且可以很好地使用 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));
                }
            }
        }
    
    }
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2015-08-30
      相关资源
      最近更新 更多