JavaFX和CDI:如何注入许多阶段

时间:2017-12-01 10:37:18

标签: javafx-8 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

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

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

感谢的!

1 个答案:

答案 0 :(得分:5)

您不需要使用CDI来管理阶段:阶段本身只有Scene;它们与您需要管理的任何其他对象没有任何依赖关系。您需要做的就是确保FXMLLoadercontrollerFactory从DI框架中检索控制器实例。

这是一个简单的例子(警告:我之前从未使用过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上的控制器工厂。可以手动创建舞台&#34;。

这是用于添加新名称的对话框的控制器。请注意它是如何通过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的类,它已经包含了控制器工厂等等。