【问题标题】:JavaFX - FXML: initialize() does not seem to recognize instance variablesJavaFX - FXML:initialize() 似乎无法识别实例变量
【发布时间】:2016-09-14 01:34:02
【问题描述】:

请求解释。
如果解释在网络上的其他地方,我找不到它。 这个问题似乎适用于各种 FXML 自定义小部件。

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

在构造函数中完成变量初始化后,一切正常。 从构造函数中移动初始化的想法 “initialize()”方法在当时看起来不错。 主要是为了以防未来可能持有更多尚未准备好的变量 在构造函数运行之后。

使用“initialize()”方法出现了一些我不完全理解的东西 如这里的代码所示。

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

但是,如果取消“initialize()”方法并尝试运行 该程序,它在一个简单的实例变量上死于 NullPointerException。 实际程序无法识别 HashMap 但此处的 PrintStream 在发布的代码中减少混乱。

在各种地方有或没有@FXML注释都会出现问题 和组合。

似乎有多种可能的失败原因,可能包括以下。
1. “initialize()”在阅读了它的描述后并没有像我认为的那样工作。
2. "initialize()" 和进程线程之间没有对话?
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   ;
  }

【问题讨论】:

  • 你有set System.out to null吗?否则 - 请尝试提供MCVE
  • Sillyfly,幸运的是,在我看来,这不是问题所在。除了 CSS 文件,原始代码是我可以使用的最小代码,但仍然会出现问题。测试程序需要模拟在另一个 FXML 应用程序中使用自定义小部件,超类 UserControl 与子类的交互是主要问题。我试图通过注释掉一小段来简化它。您的评论,真的问我为什么设置尚未完成时子类实例变量为空。在子类中设置任何实例变量之前发生错误。在下面回答?

标签: initialization javafx-8 instance-variables


【解决方案1】:

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

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

这是小径。

DirectorySelectionWidgetsTest => start(),输入
UserControl() => 构造函数,已输入
DirectorySelectionWidgets.initialize() => initialize(),在 UNEXPECTED CALL HERE
内 UserControl() => 构造函数,结束
DirectorySelectionWidgets => 构造函数,输入 预期 initialize() 在这里调用
DirectorySelectionWidgetsTest => 开始(),结束

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

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

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

【讨论】:

    猜你喜欢
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2014-10-29
    相关资源
    最近更新 更多