我正在使用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执行相同的过程没有问题。
它为什么会发生,解决方案是什么。请帮我解决一下这个。
谢谢。
答案 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(...)
实例。