将复合对象(带有 TextFields 的 GridPane)插入 ScrolPane

问题描述 投票:0回答:1

我有一个类,它使用 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);
            }
        }            
    }    
}
java javafx textfield gridpane
1个回答
0
投票

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

}

这给出了所需的结果:

Screenshot of the grid of text fields displayed in a window


对您的代码的其他评论:

注意,无需在类中复制值

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

	
© www.soinside.com 2019 - 2024. All rights reserved.