我有一个类,它使用 GridPane 构建带有 TextFields 数组的网格。我需要将此网格插入到仅接受 setContent() 方法中的 Node 的 ScrollPane 中。所以我从 GridPane 扩展了这个类。通过 MainViewController.java 类的 onMnuItemNewAction 方法在 ScrollPane 中实例化并设置 Grid 类,但未显示网格。感谢您的帮助。
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane prefHeight="277.0" prefWidth="495.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="br.com.ablogic.crossword.MainViewController">
<top>
<VBox prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<MenuBar fx:id="mnuBar" prefHeight="25.0" prefWidth="360.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="mnuItemNew" mnemonicParsing="false" onAction="#onMnuItemNewAction" text="New grid" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</VBox>
</top>
<center>
<ScrollPane fx:id="scpGrid" fitToHeight="true" fitToWidth="true" pannable="true" style="-fx-background-color: #dbbb92; -fx-background: #dbbb92;" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("MainView.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 800, 600);
stage.setTitle("Grid Demo");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
MainViewController.java(调用方法)
import javafx.geometry.Pos;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class MainViewController implements Initializable {
@FXML
private MenuItem mnuItemNew;
@FXML
private ScrollPane scpGrid;
@FXML
public void onMnuItemNewAction() {
int cols = 10;
int rows = 10;
int horizontalGap = 1;
int verticalGap = 1;
int fieldHorizontalSize = 40;
int fieldVerticalSize = 40;
var newGrid = new Grid(cols, rows, horizontalGap, verticalGap, fieldHorizontalSize, fieldVerticalSize);
scpGrid.setContent(newGrid);
newGrid.setAlignment(Pos.CENTER);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
}
}
Grid.java
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class Grid extends GridPane implements Initializable {
private final int totalColumnFields;
private final int totalRowFields;
private final int horizontalGap;
private final int verticalGap;
private final int fieldHorizontalSize;
private final int fieldVerticalSize;
public Grid(int totalColumnFields, int totalRowFields, int horizontalGap, int verticalGap, int fieldHorizontalSize, int fieldVerticalSize) {
this.totalColumnFields = totalColumnFields;
this.totalRowFields = totalRowFields;
this.horizontalGap = horizontalGap;
this.verticalGap = verticalGap;
this.fieldHorizontalSize = fieldHorizontalSize;
this.fieldVerticalSize = fieldVerticalSize;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
this.setHgap(horizontalGap);
this.setVgap(verticalGap);
TextField[][] arrayLetterField = new TextField[totalColumnFields][totalRowFields];
for (int row = 0; row < totalRowFields; row++) {
for (int col = 0; col < totalColumnFields; col++) {
arrayLetterField[col][row] = new TextField();
arrayLetterField[col][row].setMinSize(fieldHorizontalSize, fieldVerticalSize);
arrayLetterField[col][row].setMaxSize(fieldHorizontalSize, fieldVerticalSize );
this.add(arrayLetterField[col][row], col, row);
}
}
}
}
Initializable
接口及其相应的 void initialize(URL, ResourceBundle)
方法旨在供充当 FXML 文档控制器的控制器类使用。当 FXMLLoader
加载在其 fx:controller
属性中指定类的 FXML 文件时,FXMLLoader
实例化该类,然后调用其上的 initialize(...)
方法。1
您的
Grid
类不是任何 FXML 文档的控制器类。它不是由 FXMLLoader
实例化的(您可以通过在 var newGrid = new Grid(...)
类中调用 MainViewController
直接实例化它),因此在任何时候都不会自动为您调用 initialize(...)
方法。
因此,永远不会调用
initialize()
中的 Grid
方法,因此永远不会创建文本字段,也永远不会将其添加到网格窗格中。因此,您添加到滚动窗格的网格是空的,什么也看不见。
Grid
类不需要实现Initializable
,因为它不与FXML文件关联。 initialize()
方法中的代码是您希望在创建Grid
时执行的代码,因此将其移动到构造函数中:
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
public class Grid extends GridPane {
private final int totalColumnFields;
private final int totalRowFields;
private final int horizontalGap;
private final int verticalGap;
private final int fieldHorizontalSize;
private final int fieldVerticalSize;
public Grid(int totalColumnFields, int totalRowFields, int horizontalGap, int verticalGap, int fieldHorizontalSize, int fieldVerticalSize) {
this.totalColumnFields = totalColumnFields;
this.totalRowFields = totalRowFields;
this.horizontalGap = horizontalGap;
this.verticalGap = verticalGap;
this.fieldHorizontalSize = fieldHorizontalSize;
this.fieldVerticalSize = fieldVerticalSize;
this.setHgap(horizontalGap);
this.setVgap(verticalGap);
TextField[][] arrayLetterField = new TextField[totalColumnFields][totalRowFields];
for (int row = 0; row < totalRowFields; row++) {
for (int col = 0; col < totalColumnFields; col++) {
arrayLetterField[col][row] = new TextField();
arrayLetterField[col][row].setMinSize(fieldHorizontalSize, fieldVerticalSize);
arrayLetterField[col][row].setMaxSize(fieldHorizontalSize, fieldVerticalSize );
this.add(arrayLetterField[col][row], col, row);
}
}
}
}
这给出了所需的结果:
对您的代码的其他评论:
注意,无需在类中复制值
horizontalGap
和 verticalGap
,因为它们已经存储为从 hgap
继承的 vgap
和 GridPane
属性。因此,您可以通过以下方式稍微减少班级规模:
public class Grid extends GridPane {
private final int totalColumnFields;
private final int totalRowFields;
private final int fieldHorizontalSize;
private final int fieldVerticalSize;
public Grid(int totalColumnFields, int totalRowFields, int horizontalGap, int verticalGap, int fieldHorizontalSize, int fieldVerticalSize) {
this.totalColumnFields = totalColumnFields;
this.totalRowFields = totalRowFields;
this.fieldHorizontalSize = fieldHorizontalSize;
this.fieldVerticalSize = fieldVerticalSize;
this.setHgap(horizontalGap);
this.setVgap(verticalGap);
TextField[][] arrayLetterField = new TextField[totalColumnFields][totalRowFields];
for (int row = 0; row < totalRowFields; row++) {
for (int col = 0; col < totalColumnFields; col++) {
arrayLetterField[col][row] = new TextField();
arrayLetterField[col][row].setMinSize(fieldHorizontalSize, fieldVerticalSize);
arrayLetterField[col][row].setMaxSize(fieldHorizontalSize, fieldVerticalSize );
this.add(arrayLetterField[col][row], col, row);
}
}
}
}
如果您需要随时引用这些值,可以使用
getHgap()
和 getVgap()
来实现。
我还建议不要在这里子类化
GridPane
。当您向现有类添加功能时,应该保留子类化现有类。这里您实际上只是配置现有类的一个实例。子类化 GridPane
还会向应用程序的其余部分公开布局策略的内部细节,如果您想这样做,可能会使以后更改布局(例如更改为 TilePane
或其他策略)变得更加困难。我建议在这里“支持聚合而不是继承”,并且只允许访问聚合的GridPane
,而不暴露您正在使用的布局的详细信息:
import javafx.scene.Node;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
public class Grid {
private final int totalColumnFields;
private final int totalRowFields;
private final int fieldHorizontalSize;
private final int fieldVerticalSize;
private final GridPane grid;
public Grid(int totalColumnFields, int totalRowFields, int horizontalGap, int verticalGap, int fieldHorizontalSize, int fieldVerticalSize) {
this.totalColumnFields = totalColumnFields;
this.totalRowFields = totalRowFields;
this.fieldHorizontalSize = fieldHorizontalSize;
this.fieldVerticalSize = fieldVerticalSize;
grid = new GridPane();
grid.setHgap(horizontalGap);
grid.setVgap(verticalGap);
TextField[][] arrayLetterField = new TextField[totalColumnFields][totalRowFields];
for (int row = 0; row < totalRowFields; row++) {
for (int col = 0; col < totalColumnFields; col++) {
arrayLetterField[col][row] = new TextField();
arrayLetterField[col][row].setMinSize(fieldHorizontalSize, fieldVerticalSize);
arrayLetterField[col][row].setMaxSize(fieldHorizontalSize, fieldVerticalSize );
grid.add(arrayLetterField[col][row], col, row);
}
}
}
public Node getView() {
return grid;
}
}
然后对客户端代码进行轻微相应的更改:
@FXML
public void onMnuItemNewAction() {
int cols = 10;
int rows = 10;
int horizontalGap = 1;
int verticalGap = 1;
int fieldHorizontalSize = 40;
int fieldVerticalSize = 40;
var newGrid = new Grid(cols, rows, horizontalGap, verticalGap, fieldHorizontalSize, fieldVerticalSize);
var gridView = newGrid.getView();
scpGrid.setContent(gridView);
gridView.setStyle("-fx-alignment: center;");
}
(1) 请注意,从 JavaFX 2.1 开始,
Initializable
接口本质上是多余的。来自文档:
注意 此接口已被自动注入
和location
属性到控制器中所取代。resources
现在将自动调用控制器定义的任何适当注释的无参数FXMLLoader
方法。建议尽可能使用注射方式。initialize()
这意味着即使是 FXML 文档的控制器类也不需要实现
Initializable
。如果您需要在注入 @FXML
带注释的字段后执行初始化,只需定义一个无参数 initialize()
方法即可。如果你对它进行注释,你甚至可以使这个方法private
,更好地执行封装。如果您需要访问 @FXML
或 location
属性,可以按照与 FXML 文件元素相同的方式注入这些属性。例如:resources