问题描述
我想集成JavaFX和CDI。网上有一些很好的例子,比如这些:
https://dzone.com/articles/fxml-javafx-powered-cdi-jbosshttp://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:如何注入多个阶段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!