2016-05-17 49 views
0

请求解释。
如果解释是在网络上的其他地方,我找不到它。 这个问题似乎适用于各种FXML定制小部件。JavaFX - FXML:initialize()似乎无法识别实例变量

这是一个工作程序模块的相当简化。 最初的想法是在自定义窗口小部件控制器中初始化并使用一些实例变量 。

当在构造函数中完成变量初始化时,所有工作都很好。 将构造函数 中的初始化移动到“initialize()”方法的想法在当时看起来很好。 主要是,如果将来可能会有更多的变量在构造函数运行之后还没有准备好,直到 。

使用“initialize()”方法浮出水面,我不完全理解 ,如代码所示。

“initialize()”方法似乎无法识别实例变量。 提供的代码处于工作形式,即小部件出现并工作。 这是不好的东西被评论出来,所以人们可以看到它的作品。

但是,如果取消注释“initialize()”方法并尝试运行 程序,则它将在简单实例变量上因NullPointerException而死亡。 实际的程序未能识别HashMap,但PrintStream在这里 使发布的代码中的混乱更少。

问题发生在有或没有@FXML注释的各种地方 及其组合中。

似乎有各种可能的失败原因,可能包括以下几点。
1.“initialize()”在阅读其描述后并不按照我认为的方式工作。
2.“初始化()”和进程线程不相互交谈?
3.从超类派生的自定义窗口小部件控制器弄乱了事情?
4.这些测试是在NetBeans 8.0.2中使用Java 8运行的,并将其混淆?但那么问题就变成了为什么?
5.注解与子分类不协调吗?
6.上述组合或完全不同的东西?

定制控制Java代码,DirectorySelectionWidgets.java:

package blogpost ; 

// Java imports 
import java.io .PrintStream ; 

// JavaFX imports 
import javafx.event   .ActionEvent ; 
import javafx.scene.control .Button  ; 
import javafx.scene.control .Control  ; 
import javafx.fxml   .FXML  ; 

public class DirectorySelectionWidgets extends UserControl 
    { 
    @FXML 
    private Button fromRootSelectionButton ; 

    /** 
    * Does not work with or without @FXML annotation. 
    */ 
// @FXML 
    protected PrintStream out = System.out ; 

    /** 
    * UNCOMMENT method to see the NullPointerException on instance variable. 
    * The fuller version failed on important variables. 
    * <P> 
    * Does not work with or without @FXML annotation. 
    */ 
// @FXML 
// public void initialize() 
//  { out.println("HERE just entered initialize()") ; } 

    public DirectorySelectionWidgets() 
    { super() ; } 

    @FXML 
    private void handleRootSelectionRequest(ActionEvent actionEvent) 
     { 
     Control control = (Control) actionEvent.getSource() ; 
     out.println( 
      "HERE control inside handleRootSelectionRequest control =>\n " 
      + control 
        ) ; 
     } 
    } 

自定义窗口小部件FXML文件,DirectorySelectionWidgets.fxml:

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

<?import java.lang.*?> 
<?import java.net.*?> 

<?import javafx.scene.control.*?> 
<?import javafx.scene.layout.*?> 
<?import javafx.scene.paint.*?> 
<?import javafx.scene.text.*?> 

<GridPane id="rootSelectorPane" fx:id="rootSelectorPane" alignment="CENTER" gridLinesVisible="false" layoutY="42.0" maxWidth="1.7976931348623157E308" prefWidth="828.0" styleClass="root-selector-pane" xmlns:fx="http://javafx.com/fxml" > 
    <children> 
    <Button id="fromRootSelectionButton" fx:id="fromRootSelectionButton" alignment="CENTER" mnemonicParsing="false" onAction="#handleRootSelectionRequest" prefWidth="168.0" styleClass="root-selector-buttons" text="Set 'From' Root Directory" textAlignment="CENTER" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.hgrow="NEVER" GridPane.rowIndex="0" GridPane.valignment="CENTER" GridPane.vgrow="NEVER" /> 
    </children> 
    <columnConstraints> 
    <ColumnConstraints fillWidth="false" halignment="LEFT" hgrow="NEVER" minWidth="-Infinity" prefWidth="166.0" /> 
    </columnConstraints> 
    <rowConstraints> 
    <RowConstraints maxHeight="-1.0" minHeight="-1.0" prefHeight="-1.0" vgrow="NEVER" /> 
    </rowConstraints> 

<stylesheets> 
    <URL value="@PuzzleCss.css" /> 
</stylesheets> 
</GridPane> 

自定义窗口小部件的超类,用户控件.java:

package blogpost ; 

/* 
* Information link as of April 2016 is 
* <A HREF="https://programmingwithpassion.wordpress.com/2013/07/07/creating-a-reusable-javafx-custom-control/"> 
*  <I>Benjamin's programming Blog</I>. 
* </A> 
* <BR> 
* Orginal copyright 2014 Benjamin Gale. 
* License document is also there inside the Java file on his blog. 
* <P> 
* Modified in accordance with license. 
*/ 
// Java imports 
import java.io   .IOException ; 
import java.util.logging .Level  ; 
import java.util.logging .Logger  ; 
import java.net   .URL   ; 

// JavaFX imports 
import javafx.fxml   .FXMLLoader ; 
import javafx.geometry  .HPos  ; 
import javafx.scene   .Node  ; 
import javafx.scene.layout .Region  ; 
import javafx.geometry  .VPos  ; 

/** 
* This is a convenience class for creating custom controls that use the 
* {@code FXMLLoader loader = new FXMLLoader() ;} 
* approach. Mainly used for custom widgets. 
* <P> 
* Just subclass this class and all the FXMLLoader work is already done. 
* <P> 
* The only file restrictions are the following. 
* <UL> 
*  <LI> 
*  The controller file and the fxml file must be in the same package. 
*  </LI> 
*  <LI> 
*  The fxml file must have the same (case sensitive) name (sans suffix) 
*   as the controller class. 
*  <BR> 
*  That is, 
*   if the controller file is named {@code MyController.java} then 
*   the fxml file must be named {@code MyController.fxml}. 
*   <BR> 
*   This also works with other JavaFX controller files; for example, 
*   {@code MyController.groovy} would work for Groovy developers. 
*  </LI> 
* </UL> 
*/ 
public abstract class UserControl extends Region 
    { 
    private final String resourcePath = "%s.fxml" ; 

    public UserControl() 
     { this.loadView() ; } 

    /** 
    * A primary purpose for this class, 
    * make creating custom controls easier. 
    */ 
    private void loadView() 
     { 
     FXMLLoader fxmlLoader = new FXMLLoader() ; 

     fxmlLoader.setController(this) ; 
     fxmlLoader.setLocation(this.getViewURL()) ; 

     try 
      { 
      Node root = (Node) fxmlLoader.load() ; 
      setMaxSize(root) ; 

      this.getChildren().add(root) ; 
      } 
     catch (IOException ioException) 
      { 
      Logger.getLogger(UserControl.class.getName()) 
       .log(Level.SEVERE, null, ioException) ; 
      } 
     } 

    private String getViewPath() 
     { return String.format(resourcePath, this.getClass().getSimpleName()) ; } 

    private URL getViewURL() 
     { return this.getClass().getResource(this.getViewPath()) ; } 

    @Override 
    protected void layoutChildren() 
     { 
     getChildren().stream().forEach(
      (node) -> 
       { layoutInArea(node, 0, 0, 
           getWidth(), getHeight(), 
           0, 
           HPos.LEFT, VPos.TOP 
          ) ; 
       } 
     ) ; 
     } 

    private void setMaxSize(Node node) 
     { 
     if (node != null && node instanceof Region) 
      { 
      Region region = (Region) node ; 
      region.setMaxWidth( Double.MAX_VALUE) ; 
      region.setMaxHeight(Double.MAX_VALUE) ; 
      } 
     } 
    } 

测试Java代码DirectorySelectionWidgetsTest。Java的:

package blogpost ; 

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

public class DirectorySelectionWidgetsTest extends Application 
    { 
    // Written this way while reducing code to smaller size and new locations. 
    protected String fxmlFullFileName = "" 
        + "blogpost" 
        + "/" 
        + "DirectorySelectionWidgetsTest" 
        + "." 
        + "fxml" ; 

    protected String mainTitle = "Test directory selection widgets" ; 

    @Override 
    public void start(Stage stage) 
     throws Exception 
     { 
     FXMLLoader fxmlLoader = new FXMLLoader() ; 
      fxmlLoader.setController(this)  ; 

     Parent root = fxmlLoader.load( 
      getClass().getClassLoader().getResource(fxmlFullFileName) 
            ) ; 

     Scene scene = new Scene(root) ; 
      stage.setTitle(mainTitle) ; 
      stage.setScene(scene)  ; 

     stage.show() ; 
     } 

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

测试FXML文件,DirectorySelectionWidgetsTest.fxml:

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

<?import java.lang.*?> 
<?import java.net.*?> 
<?import java.util.*?> 
<?import javafx.scene.*?> 
<?import javafx.scene.control.*?> 
<?import javafx.scene.layout.*?> 

<?import blogpost.*?> 

<AnchorPane id="anchorPane" fx:id="anchorPane" styleClass="header-title-pane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> 
    <children> 
    <DirectorySelectionWidgets id="selectionWidgets" fx:id="selectionWidgets" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> 
    </children> 
    <stylesheets> 
    <URL value="@PuzzleCss.css" /> 
    </stylesheets> 
</AnchorPane> 

CSS文件,PuzzleCss.css:

.root-selector-buttons 
    { 
    -fx-background-color : 
     green, linear-gradient(to bottom right, #FFFF00 40%, #99FF33 100%) ; 
    -fx-text-fill : black ; 
    } 

.root-selector-pane 
    { 
    -fx-background-color : #DDFFBB ; 
    -fx-border-color  : #DDFFBB ; 
    } 

.rootSelectorTextFields 
    { 
    -fx-border-color : #00BB00 ; 
    -fx-text-fill : black ; 
    } 
+0

是否有可能你有[设置System.out为空](http://stackoverflow.com/questions/18384033/what-are-the-scenarios-where-system-object-might-throw-a-null终场前-除外)?否则 - 请尝试提供一个[MCVE](http://stackoverflow.com/help/mcve) – Itai

+0

幸运的是,对于我的自我而言​​,Sillyfly并不是问题所在。除CSS文件外,原始代码是我可以使用的最小代码,并且仍然存在问题。测试程序需要模仿在另一个FXML应用程序中使用自定义小部件,超类UserControl与子类交互是主要问题。我试图通过短暂的评论来简化它。您的评论,真的问到为什么当设置尚未完成时,子类实例变量为空。在子类中设置任何实例变量之前发生错误。下面回答? – user2460422

回答

0

我认为答案是一个意外的启动顺序。
我期望通常的构造函数流程;超类(UserControl)的构造函数 - >子类(DirectorySelectionWidgets)的构造函数(定义,子类变量,然后运行构造函数代码) - >等。

我得到的是子类方法initialize )在运行子类构造函数之前在超类构造函数操作中被调用。
注意:initialize()是子类DirectorySelectionWidgets中的一个方法。

这里是线索。

DirectorySelectionWidgetsTest =>开始(),输入
用户控件()=>构造,进入
DirectorySelectionWidgets.initialize()=>初始化(),内部意外CALL该处
用户控件()=>构造,结束
DirectorySelectionWidgets =>构造,进入预期初始化()调用后该处
DirectorySelectionWidgetsTest =>开始(),端

因此,DirectorySelectionWidgets局部变量在运行子类方法initialize()时未定义。

一种推测是,这是FXML基础设施跳到子类初始化()方法的结果,只要子类分层结构中的顶层构造函数完成?对于我而言,DirectorySelectionWidgetsTest 开始()方法的作用可能对此有所贡献;再次研究。

任何进一步的见解将是最受欢迎的,但意想不到的启动顺序肯定在我的困惑中发挥了作用。