【问题标题】:JavaFX and CDI: How to Inject many StagesJavaFX 和 CDI:如何注入多个阶段
【发布时间】:2018-05-15 10:46:36
【问题描述】:

我想集成 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

但是,我看到的所有示例在现实世界中都不起作用,因为它们无法注入多个 Stage(primaryStage),如果可以,我不知道如何。

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

谢谢!

【问题讨论】:

  • 将舞台注入任何东西似乎很奇怪 - 您提供的两个链接都没有使用 DI 框架来管理舞台。我看不出任何组件会依赖于舞台的任何理由。通常在 JavaFX 中使用依赖注入框架将模型和服务注入到控制器中,并在模型和服务之间建立其他依赖关系。
  • James_D,感谢您的回复!问题是为了显示模态对话框,我需要一个新的舞台,因此,问题是我怎样才能得到那个舞台?如果我调用 new Stage(),CDI bean 将不会被注入到该 Stage 的控制器中。
  • 只创建一个新阶段。为什么需要 DI 框架来创建它?
  • “如果我调用 new Stage(),CDI bean 将不会被注入到那个 Stage 的控制器中。”。这没有意义。当您调用 new Stage() 时不会创建控制器,它们(通常)是在您加载 FXML 文件时创建的。只需指示FXMLLoader 以通常的方式通过 CDI 创建控制器。
  • "它们(通常)是在您加载 FXML 文件时创建的" 是的!那么我如何将任何 CDI bean(例如服务)注入到这个控制器中 - 如果 - 它的 FXML 表示具有自己的 Stage 的模式窗口?

标签: javafx-8 cdi


【解决方案1】:

您不需要使用 CDI 来管理阶段:阶段本身只需一个 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 的类,它已经包含控制器工厂等。

【讨论】:

    猜你喜欢
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 2021-05-29
    • 2017-10-25
    相关资源
    最近更新 更多