我想整合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(例如在模态窗口中使用......)
感谢的!
答案 0 :(得分:5)
您不需要使用CDI来管理阶段:阶段本身只有Scene
;它们与您需要管理的任何其他对象没有任何依赖关系。您需要做的就是确保FXMLLoader
有controllerFactory
从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的类,它已经包含了控制器工厂等等。