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