JavaFX和CDI:如何注入多个阶段 [英] JavaFX and CDI: How to Inject many Stages

查看:20
本文介绍了JavaFX和CDI:如何注入多个阶段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想集成JavaFX和CDI。网上有一些很好的例子,比如这些:

https://dzone.com/articles/fxml-javafx-powered-cdi-jboss http://fxapps.blogspot.com.br/2017/10/using-cdi-20-in-javafx-application.html

然而,我看到的所有示例在现实世界中都不起作用,因为它们不能注入多个阶段(主阶段),如果是这样,我不知道如何操作。

所以我想知道在一个JavaFX/CDI项目中是否可以注入多个阶段(例如在模式窗口中使用...)

谢谢!

推荐答案

您不需要使用cdi来管理Stage:Stage本身只有一个Scene;它们对您需要管理的任何其他对象没有任何依赖关系。您所需要做的就是确保FXMLLoader具有从DI框架检索控制器实例的controllerFactory

这里有一个简单的例子(注意:我以前从未使用过CDI/Weld,因此我可能没有在这里进行操作的最佳方法)。

首先,公开一个获得适当控制器的控制器工厂可能是个好主意:

package app;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

import javafx.util.Callback;

@ApplicationScoped
public class CDIControllerFactory implements Callback<Class<?>, Object> {

    @Inject
    private Instance<Object> instance ;

    @Override
    public Object call(Class<?> type) {
        Object controller = instance.select(type).get();
        return controller;
    }

}

下面是我们希望与所有控制器共享的模型类。因为我们只需要一个实例,所以我们将其设置为@ApplicationScoped

package app;

import javax.enterprise.context.ApplicationScoped;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

@ApplicationScoped
public class Model {

    private final ObservableList<String> names = FXCollections.observableArrayList();

    public ObservableList<String> getNames() {
        return names ;
    }

    public void addName(String name) {
        names.add(name);
    }
}

测试应用程序将只有一个列表视图(带有名称列表)和一个用于从对话框添加新名称的按钮。以下是主控制器:

package app;

import java.io.IOException;

import javax.inject.Inject;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class MainController {

    @Inject 
    private Model model ;
    @Inject 
    private CDIControllerFactory controllerFactory ;

    @FXML
    private ListView<String> listView ;

    @FXML
    private void initialize() {
        listView.setItems(model.getNames());
    }

    @FXML
    private void showAddDialog() throws IOException {
        FXMLLoader loader = new FXMLLoader(AddNameController.class.getResource("AddNameDialog.fxml"));
        loader.setControllerFactory(controllerFactory);
        Scene scene = new Scene(loader.load());
        Stage stage = new Stage();
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.setScene(scene);
        stage.show();
    }
}
注意它如何使用FXMLLoader上的控制器工厂。舞台可以"手工"创建。

下面是用于添加新名称的对话框的控制器。注意它如何通过CDI引用相同的模型实例:

package app;

import javax.enterprise.inject.Default;
import javax.inject.Inject;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;

@Default
public class AddNameController {

    @Inject
    private Model model ;

    @FXML
    private TextField nameField  ;

    @FXML
    private void submit() {
        model.addName(nameField.getText());
        close();
    }

    @FXML
    private void close() {
        nameField.getScene().getWindow().hide();
    }
}

这里是两个FXML文件(它们都在app包中:我编码这些文件的唯一真正要求是它们应该与它们对应的控制器类在同一个包中)。

Main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
    <center>
        <ListView fx:id="listView" />
    </center>
    <bottom>
        <HBox alignment="CENTER">
            <padding>
                <Insets top="5" right="5" left="5" bottom="5" />
            </padding>
            <Button text="Add..." onAction="#showAddDialog" />
        </HBox>
    </bottom>
</BorderPane>

AddNameDialog.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
    <center>
        <ListView fx:id="listView" />
    </center>
    <bottom>
        <HBox alignment="CENTER">
            <padding>
                <Insets top="5" right="5" left="5" bottom="5" />
            </padding>
            <Button text="Add..." onAction="#showAddDialog" />
        </HBox>
    </bottom>
</BorderPane>

下面是应用程序类:

package app;

import java.io.IOException;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    private Weld weld ;
    private WeldContainer container ;

    @Override
    public void init() {
        weld = new Weld();
        container = weld.initialize();
    }

    @Override
    public void stop() {
        weld.shutdown();
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader loader = new FXMLLoader(MainController.class.getResource("Main.fxml"));
        loader.setControllerFactory(container.select(CDIControllerFactory.class).get());
        Scene scene = new Scene(loader.load(), 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

当然还有CDI配置类META-INF/beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
        bean-discovery-mode="all">
</beans>

如果你真的想让CDI提供你的舞台,你可以,但我真的看不到这样做有什么好处。但是,例如,您可以这样做:

package app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD,
        ElementType.TYPE, ElementType.PARAMETER})
public @interface ModalStage { }

它允许您提供模式阶段和非模式阶段:

package app;

import javax.enterprise.inject.Produces;

import javafx.stage.Modality;
import javafx.stage.Stage;

public class StageProducer {

    @Produces
    public Stage stage() {
        return new Stage();
    }

    @Produces
    @ModalStage
    public Stage modalStage() {
        Stage stage = stage();
        stage.initModality(Modality.APPLICATION_MODAL);
        return stage ;
    }
}

然后您的MainController可能看起来像

public class MainController {

    @Inject 
    private Model model ;
    @Inject 
    private CDIControllerFactory controllerFactory ;

    @Inject
    @ModalStage
    private Stage addNameDialogStage ;

    @FXML
    private ListView<String> listView ;

    @FXML
    private void initialize() {
        listView.setItems(model.getNames());
    }

    @FXML
    private void showAddDialog() throws IOException {
        FXMLLoader loader = new FXMLLoader(AddNameController.class.getResource("AddNameDialog.fxml"));
        loader.setControllerFactory(controllerFactory);
        Scene scene = new Scene(loader.load());
        addNameDialogStage.setScene(scene);
        addNameDialogStage.show();
    }
}

您还可以轻松地将其他功能构建到其中,例如,提供一个用于从资源名称加载FXML的类,该类已经合并了控制器工厂,等等。

这篇关于JavaFX和CDI:如何注入多个阶段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆