JavaFX+Afterburner.fx 从子访问父 window
JavaFX+Afterburner.fx access parent window from child
我有一个具有这种 window 结构的程序:
左侧有按钮和绿色区域不同的地方windows改变了用户点击左按钮的位置。
我有一个主 class(称为 Estructura),它充当主 window 的控件,绿色面板除外。对于绿色面板,我从左侧根据用户按钮 selection 注入(感谢 afterburner.fx DI)对应的面板。由于基于 MVP 的 DI 框架,注入的面板有自己的惰性控制器。
public class EstructuraPresenter implements Initializable {
private static final Logger LOG = getLogger(EstructuraPresenter.class.getName());
@FXML
ToggleButton btnPlantillas, btnAlumnos, btnEstadisticas;
@FXML
BorderPane pEstructura; //Correspond to all window except green zone
@FXML
StackPane pContenedor; //Green zone
//Injection of all child windows that i want to show in green zone
@Inject
private AlumnosView alumnosview;
@Inject
private PlantillasView plantillasview;
@Inject
private EstadisticasView estadisticasview;
@Override
public void initialize(URL url, ResourceBundle rb) {
//I set up green zone with alumnos panel so I call method whit request section to load
cambiarSeccion("Alumnos");
btnPlantillas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Plantillas");
});
btnAlumnos.setOnAction((ActionEvent event) -> {
cambiarSeccion("Alumnos");
});
btnEstadisticas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Estadisticas");
});
}
//I pass to this method, name of window/panel that I like to load in green zone
public void cambiarSeccion(String nombreVentana) {
try {
//First, reset all buttons (so when user select an option and enter in case, I select option button making like effect as a selected)
btnAlumnos.setSelected(false);
btnPlantillas.setSelected(false);
btnEstadisticas.setSelected(false);
switch (nombreVentana) {
case "Alumnos":
if (btnAlumnos.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Alumnos");
btnAlumnos.setSelected(true);
//I need to do this check because for first time program load because green zone hasn't got any previous panel load
if (pContenedor.getChildren().contains(alumnosview.getView())) {
pContenedor.getChildren().clear();
alumnosview.getViewAsync(pContenedor.getChildren()::add);
} else {
pContenedor.getChildren().add(alumnosview.getView());
}
}
break;
case "Plantillas":
if (btnPlantillas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Plantillas");
btnPlantillas.setSelected(true);
pContenedor.getChildren().clear();
plantillasview.getViewAsync(pContenedor.getChildren()::add);
}
break;
case "Estadisticas":
if (btnEstadisticas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Estadísticas");
btnEstadisticas.setSelected(true);
pContenedor.getChildren().clear();
estadisticasview.getViewAsync(pContenedor.getChildren()::add);
}
break;
//There are more cases but I use same configuration like above examples...
}
}
catch (Exception e) {
LOG.log(Level.SEVERE, e.toString());
new Dialogos().mostrarExcepcion(null, e);
}
}
}
好吧,当用户点击左键时,面板在绿色区域加载正常。但是我遇到的问题是用户例如在 button/link...在 EstructuraPresenter 控制器中。
因此,例如,在用户 select estadisticas 按钮的情况下(我隐藏在图像中,对不起,我试图简化),这是 estadisticaspresenter 控制器:
public class EstadisticasPresenter implements Initializable {
private static final Logger LOG = getLogger(EstadisticasPresenter.class.getName());
@FXML
Hyperlink linkTotalAlumnos;
@Inject
private EstructuraView estructuraview;
@Override
public void initialize(URL url, ResourceBundle rb) {
linkTotalAlumnos.setOnAction((ActionEvent event) -> {
//This call apparently works, I debug and call to method cambiarSeccion happend but screen isn't update
((EstructuraPresenter) estructuraview.getPresenter()).cambiarSeccion("Alumnos");
});
}
}
加载 estadisticas window 的位置如果我单击左侧按钮,则可以正常工作,但如果我单击绿色加载面板,则没有任何反应。我在下一张图片中恢复问题:
在我看来,您的方法有一个问题:当您召集 children 演示者中的任何一位时
((EstructuraPresenter) estructuraview.getPresenter())
.cambiarSeccion("Alumnos");
estructuraview.getPresenter()
实际上是在创建主演示者的新实例。
这意味着当你点击超链接时 EstructuraPresenter.initialize()
被再次调用,并且你有两次调用 cambiarSeccion()
(一次来自 Initialize
,一次来自操作),将 children 添加到新实例,而不是旧实例,也就是您看到的那个。这就是为什么您看不到任何变化!
我建议采用不同的方法:让主要演示者聆听其 childrens 上某些属性的变化。
例如,添加一个布尔值 属性 以通知点击超链接:
public class EstadisticasPresenter implements Initializable {
@FXML Hyperlink linkTotalAlumnos;
private final BooleanProperty link = new SimpleBooleanProperty();
public boolean isLink() { return link.get(); }
public void setLink(boolean value) { link.set(value); }
public BooleanProperty linkProperty() { return link; }
@Override public void initialize(URL url, ResourceBundle rb) {
linkTotalAlumnos.setOnAction((ActionEvent event) -> {
link.set(true);
});
}
}
主讲人:
@Override
public void initialize(URL url, ResourceBundle rb) {
...
EstadisticasPresenter estadisticas =
(EstadisticasPresenter)estadisticasview.getPresenter();
estadisticas.linkProperty().addListener((ob,b,b1)->{
if(b1){
cambiarSeccion("Alumnos");
// reset link property
estadisticas.setLink(false);
}
});
}
作为旁注,我会使用 pContenedor.getChildren().setAll(<view>);
而不是 clear()
和 add
,因为您一次只显示一个 child。
根据@josé-pereda的回答,终于可以linkbutton/hyperlink从sub-window(绿色区域)到主控制器了。我的解决方案的优点是我避免使用布尔变量。
在 EstructuraPresenter
控制器中,我将侦听器添加到来自 EstadisticasPresenter
的按钮(这是来自子 window 之一的控制器):
public class EstructuraPresenter implements Initializable {
private static final Logger LOG = getLogger(EstructuraPresenter.class.getName());
@FXML
ToggleButton btnAlumnos, btnEstadisticas;
@FXML
StackPane pContenedor;
@Inject
private AlumnosView alumnosview;
@Inject
private EstadisticasView estadisticasview;
@Inject
private DataModel datos;
@Override
public void initialize(URL url, ResourceBundle rb) {
((EstadisticasPresenter) estadisticasview.getPresenter()).getLinkTotalAlumnos().setOnAction((ActionEvent event) -> {
this.cambiarSeccion("Alumnos");
});
//This call is from first time, where program starts to show by default Alumnos sub-window
cambiarSeccion("Alumnos");
btnAlumnos.setOnAction((ActionEvent event) -> {
cambiarSeccion("Alumnos");
});
btnEstadisticas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Estadisticas");
});
}
public void cambiarSeccion(String nombreVentana) {
try {
btnAlumnos.setSelected(false);
btnEstadisticas.setSelected(false);
switch (nombreVentana) {
case "Alumnos":
if (btnAlumnos.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Alumnos");
btnAlumnos.setSelected(true);
alumnosview.getViewAsync(pContenedor.getChildren()::setAll);
pContenedor.getChildren().setAll(alumnosview.getView());
}
break;
case "Estadisticas":
if (btnEstadisticas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Estadísticas");
btnEstadisticas.setSelected(true);
estadisticasview.getViewAsync(pContenedor.getChildren()::setAll);
}
break;
}
}
catch (Exception e) {
LOG.log(Level.SEVERE, e.toString());
new Dialogos().mostrarExcepcion(null, e);
}
}
}
NOTE 另外我把getChildren().clear()
&& getChildren().add()
改成getChildren().setAll()
;这些可以防止我在绿色区域中放置的每个 window 重新初始化控制器。
estadisticasview.getViewAsync(pContenedor.getChildren()::setAll);
并且在 EstadisticasPresenter 控制器中,我从该控件中添加了吸气剂,我希望用户点击该控件可以转到另一个 window(因此当用户点击控件时,绿色区域会随另一个 window/pane 变化) .
public class EstadisticasPresenter implements Initializable {
private static final Logger LOG = getLogger(EstadisticasPresenter.class.getName());
@FXML
Hyperlink linkTotalAlumnos;
@Inject
private DataModel datamodel;
@Override
public void initialize(URL url, ResourceBundle rb) {
}
//Getters that I use in EstructuraPresenter controller to set up event handlers
public Hyperlink getLinkTotalAlumnos() {
return linkTotalAlumnos;
}
}
我有一个具有这种 window 结构的程序:
左侧有按钮和绿色区域不同的地方windows改变了用户点击左按钮的位置。
我有一个主 class(称为 Estructura),它充当主 window 的控件,绿色面板除外。对于绿色面板,我从左侧根据用户按钮 selection 注入(感谢 afterburner.fx DI)对应的面板。由于基于 MVP 的 DI 框架,注入的面板有自己的惰性控制器。
public class EstructuraPresenter implements Initializable {
private static final Logger LOG = getLogger(EstructuraPresenter.class.getName());
@FXML
ToggleButton btnPlantillas, btnAlumnos, btnEstadisticas;
@FXML
BorderPane pEstructura; //Correspond to all window except green zone
@FXML
StackPane pContenedor; //Green zone
//Injection of all child windows that i want to show in green zone
@Inject
private AlumnosView alumnosview;
@Inject
private PlantillasView plantillasview;
@Inject
private EstadisticasView estadisticasview;
@Override
public void initialize(URL url, ResourceBundle rb) {
//I set up green zone with alumnos panel so I call method whit request section to load
cambiarSeccion("Alumnos");
btnPlantillas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Plantillas");
});
btnAlumnos.setOnAction((ActionEvent event) -> {
cambiarSeccion("Alumnos");
});
btnEstadisticas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Estadisticas");
});
}
//I pass to this method, name of window/panel that I like to load in green zone
public void cambiarSeccion(String nombreVentana) {
try {
//First, reset all buttons (so when user select an option and enter in case, I select option button making like effect as a selected)
btnAlumnos.setSelected(false);
btnPlantillas.setSelected(false);
btnEstadisticas.setSelected(false);
switch (nombreVentana) {
case "Alumnos":
if (btnAlumnos.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Alumnos");
btnAlumnos.setSelected(true);
//I need to do this check because for first time program load because green zone hasn't got any previous panel load
if (pContenedor.getChildren().contains(alumnosview.getView())) {
pContenedor.getChildren().clear();
alumnosview.getViewAsync(pContenedor.getChildren()::add);
} else {
pContenedor.getChildren().add(alumnosview.getView());
}
}
break;
case "Plantillas":
if (btnPlantillas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Plantillas");
btnPlantillas.setSelected(true);
pContenedor.getChildren().clear();
plantillasview.getViewAsync(pContenedor.getChildren()::add);
}
break;
case "Estadisticas":
if (btnEstadisticas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Estadísticas");
btnEstadisticas.setSelected(true);
pContenedor.getChildren().clear();
estadisticasview.getViewAsync(pContenedor.getChildren()::add);
}
break;
//There are more cases but I use same configuration like above examples...
}
}
catch (Exception e) {
LOG.log(Level.SEVERE, e.toString());
new Dialogos().mostrarExcepcion(null, e);
}
}
}
好吧,当用户点击左键时,面板在绿色区域加载正常。但是我遇到的问题是用户例如在 button/link...在 EstructuraPresenter 控制器中。
因此,例如,在用户 select estadisticas 按钮的情况下(我隐藏在图像中,对不起,我试图简化),这是 estadisticaspresenter 控制器:
public class EstadisticasPresenter implements Initializable {
private static final Logger LOG = getLogger(EstadisticasPresenter.class.getName());
@FXML
Hyperlink linkTotalAlumnos;
@Inject
private EstructuraView estructuraview;
@Override
public void initialize(URL url, ResourceBundle rb) {
linkTotalAlumnos.setOnAction((ActionEvent event) -> {
//This call apparently works, I debug and call to method cambiarSeccion happend but screen isn't update
((EstructuraPresenter) estructuraview.getPresenter()).cambiarSeccion("Alumnos");
});
}
}
加载 estadisticas window 的位置如果我单击左侧按钮,则可以正常工作,但如果我单击绿色加载面板,则没有任何反应。我在下一张图片中恢复问题:
在我看来,您的方法有一个问题:当您召集 children 演示者中的任何一位时
((EstructuraPresenter) estructuraview.getPresenter())
.cambiarSeccion("Alumnos");
estructuraview.getPresenter()
实际上是在创建主演示者的新实例。
这意味着当你点击超链接时 EstructuraPresenter.initialize()
被再次调用,并且你有两次调用 cambiarSeccion()
(一次来自 Initialize
,一次来自操作),将 children 添加到新实例,而不是旧实例,也就是您看到的那个。这就是为什么您看不到任何变化!
我建议采用不同的方法:让主要演示者聆听其 childrens 上某些属性的变化。
例如,添加一个布尔值 属性 以通知点击超链接:
public class EstadisticasPresenter implements Initializable {
@FXML Hyperlink linkTotalAlumnos;
private final BooleanProperty link = new SimpleBooleanProperty();
public boolean isLink() { return link.get(); }
public void setLink(boolean value) { link.set(value); }
public BooleanProperty linkProperty() { return link; }
@Override public void initialize(URL url, ResourceBundle rb) {
linkTotalAlumnos.setOnAction((ActionEvent event) -> {
link.set(true);
});
}
}
主讲人:
@Override
public void initialize(URL url, ResourceBundle rb) {
...
EstadisticasPresenter estadisticas =
(EstadisticasPresenter)estadisticasview.getPresenter();
estadisticas.linkProperty().addListener((ob,b,b1)->{
if(b1){
cambiarSeccion("Alumnos");
// reset link property
estadisticas.setLink(false);
}
});
}
作为旁注,我会使用 pContenedor.getChildren().setAll(<view>);
而不是 clear()
和 add
,因为您一次只显示一个 child。
根据@josé-pereda的回答,终于可以linkbutton/hyperlink从sub-window(绿色区域)到主控制器了。我的解决方案的优点是我避免使用布尔变量。
在 EstructuraPresenter
控制器中,我将侦听器添加到来自 EstadisticasPresenter
的按钮(这是来自子 window 之一的控制器):
public class EstructuraPresenter implements Initializable {
private static final Logger LOG = getLogger(EstructuraPresenter.class.getName());
@FXML
ToggleButton btnAlumnos, btnEstadisticas;
@FXML
StackPane pContenedor;
@Inject
private AlumnosView alumnosview;
@Inject
private EstadisticasView estadisticasview;
@Inject
private DataModel datos;
@Override
public void initialize(URL url, ResourceBundle rb) {
((EstadisticasPresenter) estadisticasview.getPresenter()).getLinkTotalAlumnos().setOnAction((ActionEvent event) -> {
this.cambiarSeccion("Alumnos");
});
//This call is from first time, where program starts to show by default Alumnos sub-window
cambiarSeccion("Alumnos");
btnAlumnos.setOnAction((ActionEvent event) -> {
cambiarSeccion("Alumnos");
});
btnEstadisticas.setOnAction((ActionEvent event) -> {
cambiarSeccion("Estadisticas");
});
}
public void cambiarSeccion(String nombreVentana) {
try {
btnAlumnos.setSelected(false);
btnEstadisticas.setSelected(false);
switch (nombreVentana) {
case "Alumnos":
if (btnAlumnos.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Alumnos");
btnAlumnos.setSelected(true);
alumnosview.getViewAsync(pContenedor.getChildren()::setAll);
pContenedor.getChildren().setAll(alumnosview.getView());
}
break;
case "Estadisticas":
if (btnEstadisticas.isSelected() == false) {
sepTitulo.setVisible(true);
lbTitulo.setText("Estadísticas");
btnEstadisticas.setSelected(true);
estadisticasview.getViewAsync(pContenedor.getChildren()::setAll);
}
break;
}
}
catch (Exception e) {
LOG.log(Level.SEVERE, e.toString());
new Dialogos().mostrarExcepcion(null, e);
}
}
}
NOTE 另外我把getChildren().clear()
&& getChildren().add()
改成getChildren().setAll()
;这些可以防止我在绿色区域中放置的每个 window 重新初始化控制器。
estadisticasview.getViewAsync(pContenedor.getChildren()::setAll);
并且在 EstadisticasPresenter 控制器中,我从该控件中添加了吸气剂,我希望用户点击该控件可以转到另一个 window(因此当用户点击控件时,绿色区域会随另一个 window/pane 变化) .
public class EstadisticasPresenter implements Initializable {
private static final Logger LOG = getLogger(EstadisticasPresenter.class.getName());
@FXML
Hyperlink linkTotalAlumnos;
@Inject
private DataModel datamodel;
@Override
public void initialize(URL url, ResourceBundle rb) {
}
//Getters that I use in EstructuraPresenter controller to set up event handlers
public Hyperlink getLinkTotalAlumnos() {
return linkTotalAlumnos;
}
}