2016-05-17 47 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 開始()方法的作用可能對此有所貢獻;再次研究。

任何進一步的見解將是最受歡迎的,但意想不到的啓動順序肯定在我的困惑中發揮了作用。