如何在javafx中处理AnchorPane对象的空指针异常

时间:2015-08-06 08:56:04

标签: javafx

我正在使用afterburner构建一个javafx应用程序。我有一个带有AnchorPane的主窗口。在这个AnchorPane中,我有MenuBar和另一个fx:id=contentPane的AnchorPane。在这个contentPane我试图加载另一个场景,它有一个不同的Presenter给我一个NPE,而如果我这样做是一个菜单动作是好的。请参阅下面的代码和更多详细信息..

这是我的主要课程开始阶段。

public class App extends Application
{
    @Override
    public void start(Stage stage) throws Exception {
        MainView mainView = new MainView();
        Scene scene = new Scene(mainView.getView()); 
        stage.setTitle("Main");
        stage.setScene(scene);
        stage.show();
    }

    @Override
    public void stop() throws Exception { 
        Injector.forgetAll();
    }

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

这是主要演示者

public class MainPresenter implements Initializable {
    @FXML
    AnchorPane contentBox;

    private ObjectProperty<FormOpener> newFormProperty ;

    ReturnsInputView returnsView ;
    ReturnsInputPresenter returnsPresenter;
    InwardsInputView inputView;
    InwardsInputPresenter inwardsPresenter;

    @PostConstruct
    public void init(){
        this.newFormProperty = new SimpleObjectProperty();
        this.newFormProperty.addListener(new ChangeListener<FormOpener>() {
            @Override
            public void changed(ObservableValue<? extends FormOpener> observable, FormOpener oldValue, FormOpener newValue) {
                if(newValue!=null){
                    InwardsInputView inputView = new InwardsInputView();
                    inwardsPresenter = (InwardsInputPresenter) inputView.getPresenter();
                    contentBox.getChildren().add(inputView.getView());
                } 
            }
        }); 
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.returnsView = new ReturnsInputView();
        this.returnsPresenter = (ReturnsInputPresenter) returnsView.getPresenter(); 
        this.contentBox.getChildren().add(returnsView.getView());
    }

    public void showIncomingForm(){
        this.returnsView = new ReturnsInputView();
        this.returnsPresenter = (ReturnsInputPresenter) returnsView.getPresenter(); 
        contentBox.getChildren().add(returnsView.getView());
    }

    public ObjectProperty<FormOpener> newFormProperty(){
        return newFormProperty;
    }

}

这是main.fxml

<AnchorPane id="AnchorPane" minHeight="180.0" prefHeight="362.0" prefWidth="503.0" styleClass="airpad" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.qaf.MainPresenter">
    <children>
    <AnchorPane fx:id="contentBox" layoutX="1.0" layoutY="25.0" minHeight="0.0" minWidth="0.0" prefHeight="336.0" prefWidth="503.0" style="-fx-background-color: aqua;" />
        <MenuBar fx:id="menuBar" prefHeight="25.0" prefWidth="504.0">
         <menus>
            <Menu mnemonicParsing="false" text="Stock">
               <items>
                  <MenuItem fx:id="returned" mnemonicParsing="false" onAction="#showReturnedForm" text="Returned" />
               </items>
            </Menu>
         </menus>
      </MenuBar>
    </children>
</AnchorPane>

现在这里是ReturnsInputPresenter

public class ReturnsInputPresenter implements Initializable {
    @FXML
    Button saveButton;
    @FXML 
    TextField orderNo;
    @Inject
    MainPresenter main;

    private ObjectProperty<FormOpener> newFormProperty ;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.newFormProperty = new SimpleObjectProperty();
        this.newFormProperty.addListener(new ChangeListener<FormOpener>() {
            @Override
            public void changed(ObservableValue<? extends FormOpener> observable, FormOpener oldValue, FormOpener newValue) {
                if(newValue!=null){                    
                    main.newFormProperty().set(newValue);
                }
            }
        }); 
    }

    public void save() {
        FormOpener fOpener = new FormOpener();
        fOpener.setInwards(false);
        this.newFormProperty.set(fOpener); 
    }

    public ObjectProperty<FormOpener> newFormProperty(){
        return newFormProperty;
    }

这里returnsinput.fxml我有一个文本框和一个按钮。在按钮的操作中,我将newFormProperty()设置为新值,其中有一个更改newFormProperty()值的侦听器属于mainPresenter,此时我尝试访问{{1}然后它给了我一个NPE,而菜单Action执行相同的过程没有问题。 它为什么会发生,解决方案是什么。请帮我解决一下这个。                谢谢。

1 个答案:

答案 0 :(得分:0)

注入MainPresenter的{​​{1}}实例与实例ReturnsInputPresenter时为您创建的实例不同。 (想象一下,你有两个MainView实例,这是完全合理的,因此有MainView的两个实例。框架将如何知道应该将哪个实例注入MainPresenter实例?)< / p>

这里的问题实际上是你试图以不打算使用的方式使用框架。 Afterburnerfx是一个实现MVP的框架,注入机制实际上是为了注入模型的元素而设计的,因此模型实例可以由多个演示者共享。它的目的不是将演示者的引用从一个演示者转移到另一个演示者。

但实际上你所要做的就是非常适合MVP:你试图在不同的主持人之间共享一段数据(特别是ReturnsInputPresenter)。因此,只需定义一个类来保存该数据(以及您可能需要共享的其他数据):

ObjectProperty<FormOpener>

现在您的public class FormOpenerModel /* you can probably think of a better name */ { private final ObjectProperty<FormOpener> formOpener = new SimpleObjectProperty<>(); public ObjectProperty<FormOpener> formOpenerProperty() { return formOpener ; } public final FormOpener getFormOpener() { return formOpenerProperty().get(); } public final void setFormOpener(FormOpener formOpener) { formOpenerProperty().set(formOpener); } // other properties as needed... } 应该看起来像

MainPresenter

您可能不需要所有这些对其他观点和演示者等的引用,但我将它们留在了。如果您不需要它们,则应删除它们。

您的public class MainPresenter implements Initializable { @FXML AnchorPane contentBox; @Inject private FormOpenerModel formOpenerModel ; // you probably don't need any of these references any more: ReturnsInputView returnsView ; ReturnsInputPresenter returnsPresenter; InwardsInputView inputView; InwardsInputPresenter inwardsPresenter; @Override public void initialize(URL location, ResourceBundle resources) { this.returnsView = new ReturnsInputView(); this.returnsPresenter = (ReturnsInputPresenter) returnsView.getPresenter(); this.contentBox.getChildren().add(returnsView.getView()); formOpenerModel.formOpenerProperty().addListener(new ChangeListener<FormOpener>() { @Override public void changed(ObservableValue<? extends FormOpener> observable, FormOpener oldValue, FormOpener newValue) { if(newValue!=null){ InwardsInputView inputView = new InwardsInputView(); inwardsPresenter = (InwardsInputPresenter) inputView.getPresenter(); contentBox.getChildren().add(inputView.getView()); } } }); } public void showIncomingForm(){ this.returnsView = new ReturnsInputView(); this.returnsPresenter = (ReturnsInputPresenter) returnsView.getPresenter(); contentBox.getChildren().add(returnsView.getView()); } }

ReturnsInputPresenter

(顺便说一句,从版本2.0开始,FXML控制器类不需要实现public class ReturnsInputPresenter implements Initializable { @FXML Button saveButton; @FXML TextField orderNo; @Inject private FormOpenerModel formOpenerModel; @Override public void initialize(URL location, ResourceBundle resources) { } public void save() { FormOpener fOpener = new FormOpener(); fOpener.setInwards(false); formOpenerModel.formOpenerProperty().set(fOpener); } } ,所以你可以摆脱Initializable,如果implements Initializable方法是如上所述的无操作,你可以完全删除它。)

注入器非常智能,可以在注入实例并重用它们时缓存实例(即它使用相当于&#34;单例范围&#34;来自CDI或Spring),因此两位演示者都将获得对实例的引用initialize(...)实例。