JavaFX - FXML:initialize()似乎无法识别实例变量

时间:2016-05-17 19:30:34

标签: initialization javafx-8 instance-variables

要求解释。
如果解释在网络的其他地方,我找不到它。 问题似乎适用于所有类型的FXML自定义小部件。

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

在构造函数中完成变量初始化时,一切运行良好。 从构造函数移动初始化的想法  到"初始化()"方法在当时看起来很好。 主要是,如果未来可能存在更多尚未准备就绪的变量  在构造函数运行之后。

使用" initialize()"方法浮出水面,我不太了解  这里的代码就是例证。

" initialize()"方法似乎没有识别实例变量。 提供的代码是工作形式,即小部件出现并起作用。 这是不好的东西被注释掉,所以人们可以看到它的工作原理。

但是,如果取消注释" initialize()"方法和尝试运行  程序,它在一个简单的实例变量上以NullPointerException终止。  实际程序无法在此处识别HashMap而是识别PrintStream  使发布的代码更加混乱。

在各种地方使用或不使用@FXML注释会出现问题  和组合。

似乎有各种可能的失败原因,可能包括以下内容 1."初始化()"在阅读其描述后,它的工作方式与我的想法不同 2."初始化()"和进程线程没有相互通信?
3.来自超级类的自定义小部件控制器会搞砸了什么? 4.测试是在带有Java 8的NetBeans 8.0.2中运行的,这会让它变得混乱吗?但问题就变成了原因?
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>

自定义窗口小部件超类,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 ) ;
          }
      }
  }

测试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   ;
  }

1 个答案:

答案 0 :(得分:0)

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

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

这是小道。

DirectorySelectionWidgetsTest =&gt; start(),输入
UserControl()=&gt;构造函数,输入
DirectorySelectionWidgets.initialize()=&gt; initialize(),在意外呼叫中 UserControl()=&gt;构造函数,结束
DirectorySelectionWidgets =&gt;构造函数,输入 EXPECTED 初始化()在此之后调用
DirectorySelectionWidgetsTest =&gt; start(),结束

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

有一种猜测,这是FXML基础设施在子类层次结构中的顶级构造函数完成后立即跳转到子类 initialize()方法的结果? DirectorySelectionWidgetsTest start()方法的作用可能对此有所贡献,我不清楚;另一次研究。

任何进一步的见解都会受到欢迎,但意外的启动序列肯定在我的困惑中发挥了作用。