2016-05-17 15 views
0

Eine Anfrage für eine Erklärung.
Wenn die Erklärung an anderer Stelle im Internet ist, konnte ich es nicht finden. Das Problem scheint für alle Arten von benutzerdefinierten FXML-Widgets zu gelten.JavaFX - FXML: initialize() scheint keine Instanzvariablen zu erkennen

Dies ist eine erhebliche Vereinfachung eines Arbeitsprogrammmoduls. Der ursprüngliche Gedanke war, einige Instanzvariablen im benutzerdefinierten Widget-Controller zu initialisieren und zu verwenden.

Wenn die Initialisierung der Variablen im Konstruktor durchgeführt wurde, funktionierte alles gut. Die Idee, die Initialisierung vom Konstruktor in eine "initialize()" - Methode zu verschieben, erschien zu dieser Zeit gut. Hauptsächlich, wenn die Zukunft mehr Variablen enthalten könnte, die erst nach dem Ausführen des Konstruktors bis bereit sind.

Mit der "initialize()" Methode tauchte etwas auf, das ich nicht ganz verstehe wie durch den Code hier veranschaulicht.

Die Methode "initialize()" scheint Instanzvariablen nicht zu erkennen. Der Code zur Verfügung gestellt wird in funktionierender Form, das Widget erscheint und funktioniert. Das ist schlechtes Zeug ist auskommentiert nur so Leute können sehen, es funktioniert.

Wenn Sie jedoch die Methode "initialize()" auskommentieren und das Programm ausführen möchten, wird es mit einer NullPointerException für eine einfache Instanzvariable beendet. Das eigentliche Programm konnte eine HashMap nicht erkennen, aber der PrintStream hier sorgt für weniger Unordnung im gebuchten Code.

Das Problem tritt mit oder ohne @ FXML-Annotationen an allen möglichen Stellen und Kombinationen auf.

Es gibt verschiedene mögliche Gründe für den Fehler, einschließlich der folgenden.
1. "initialize()" funktioniert nicht so, wie ich es nach dem Lesen der Beschreibung glaube.
2. "initialize()" und Prozess Threads sprechen nicht miteinander?
3. Vermasselt der benutzerdefinierte Widget-Controller, der von einer Superklasse stammt, die Dinge?
4. Die Tests wurden innerhalb von NetBeans 8.0.2 mit Java 8 ausgeführt, und das vermasselt? Aber dann wird die Frage warum?
5. Anmerkungen funktionieren nicht gut mit Sub-Classing?
6. Kombinationen der oben genannten oder etwas ganz anderes?

Der Java-Code benutzerdefinierte Controller, 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 
        ) ; 
     } 
    } 

Das benutzerdefinierte Widget fxml Datei, 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> 

Das benutzerdefinierte Widget Superklasse, Usercontrol .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) ; 
      } 
     } 
    } 

Der Java-Testcode, 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) ; } 
    } 

Das Test fxml Datei, 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> 

Die CSS-Datei, 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

Ist es möglich, dass Sie [System.out auf null setzen] (http://stackoverflow.com/questions/18384033/what-are-the-scenarios-where-system-object-might-throw-a-null) -Zeiger-Ausnahme)? Andernfalls - bitte versuchen Sie, eine [MCVE] (http://stackoverflow.com/help/mcve) – Itai

+0

Sillyfly, zum Glück, für mein Ego, das war nicht das Problem. Abgesehen von der CSS-Datei war der ursprüngliche Code der minimale, den ich verwenden konnte und trotzdem das Problem auftritt. Das Testprogramm wurde benötigt, um das benutzerdefinierte Widget in einer anderen FXML-Anwendung nachzuahmen, die Superklasse UserControl, die Interaktion mit der Unterklasse war das Hauptproblem. Ich habe versucht, es leicht zu machen, indem ich ein kurzes Segment auskommentierte. In Ihrem Kommentar wurde ich wirklich gefragt, warum die Unterklasseninstanzvariable null war, wenn das Setup nicht beendet wurde. Es ist ein Fehler aufgetreten, bevor Instanzvariablen in der Unterklasse festgelegt wurden. Antwort unten? – user2460422

Antwort

0

Ich denke, die Antwort ist, eine unerwartete Startsequenz.
Ich erwartete den üblichen Konstruktorfluss; Superklasse (UserControl) -Konstruktor -> Unterklasse (DirectorySelectionWidgets) -Konstruktor (definieren, Unterklassenvariablen, dann Konstruktorcode ausführen) -> etc.

Was ich bekam, war die Unterklasse-Methode, initialisieren () wird innerhalb der Super-Klassenkonstruktoroperationen aufgerufen, bevor der Unterklassenkonstruktor ausgeführt wird.
Hinweis: initialize() ist eine Methode innerhalb der Unterklasse DirectorySelectionWidgets.

Hier ist der Weg.

DirectorySelectionWidgetsTest => start(), trat
Usercontrol() => Constructor trat
DirectorySelectionWidgets.initialize() => initialisieren() innerhalb UNERWARTETE ANRUF HIER
Usercontrol() => Constructor, Ende
DirectorySelectionWidgets => Constructor trat ERWARTET initialize() AUFRUF NACH HIER
DirectorySelectionWidgetsTest => start(), Ende

Daher sind die lokalen Variablen von DirectorySelectionWidgets nicht definiert, wenn die Unterklassenmethode initialize() ausgeführt wird.

Eine Vermutung ist, ist dies eine Folge der FXML Infrastruktur ist auf eine Unterklasse initialize Springen() Methode, sobald das oben Konstruktor in einer Sub-Klassierung Hierarchie fertig ist? Die Rolle, wie die DirectorySelectionWidgetsTest start() Methode dazu beitragen könnte, ist mir unklar; eine Studie für eine andere Zeit.

Alle weiteren Einsichten wären sehr willkommen, aber die unerwartete Startsequenz spielte sicherlich eine Rolle in der Verwirrung, die ich hatte.