将参数传递给嵌套控制器
Passing parameter to nested controller
我想问一下程序结构
I have ControllerStatistic
与 FXMLStatistic
相关,我在其中定义 TabPane
.
在 initialize
在 ControllerStatistic
我每个月都添加标签。每个选项卡都包含 FXMLTableViewMonthly
和 ControllerMonthly
。在 ControllerMonthly
中,我想用每个月的每一天的行填充 table。我有来自静态字段的月份信息:
private static int countControllers = 0;
public ControllerUdostepnianieNaZewnatrz() {
countControllers++;
monthNumber = countControllers;
}
我在 initialize
中填充 table。
这可行,但我认为这不是正确的方法。
我想将 ControllerStatistic
中的月份参数传递给 ControllerMonthly
。
我在这里看到 2 个选项:
在 ControllerStatistic
中我从加载器获取控制器并设置月份,然后在 ControllerMonthly
中我无法在 initialize
中填充(月份字段为空)所以我需要在 ControllerStatistic
设置月份字段后。
我还可以从 FXML 中删除 fx:controller
并按照@jewelsea Passing Parameters JavaFX FXML 中所述在代码中构建一个新控制器(他提到他不喜欢该解决方案)。然后我想我可以填充 ControllerMonthly
in initialize
.
我选择使用第二种方法。首先对我来说很糟糕(在设置月份后填充 - 解决方案看起来会导致很多错误)。
如何操作?
您还可以为 FXMLLoader
重新定义 ControllerFactory
。类似于:
loader.setControllerFactory((Class clazz) -> {
if (clazz.isAssignableFrom(SomeClass.class)) {
return new SomeClass(getMonthNumber());
} else {
return clazz.newInstance();
}
});
嗯,没有一般的好坏之分。这取决于您的 usecase/design 和品味。
让我们先看看其他 FX - 没有 fxml 的元素,以及如何填充它们,以走上正确的轨道。以 AnchorPane 为例。首先创建它,创建后用其他元素填充它。当你完成后,你展示了整个事情。您不要覆盖 AnchorPane 中的某些 initialize() 方法:
public void createAStage(String foo){
AnchorPane pane = new AnchorPane();
Stage stage = new Stage();
Scene scene = new Scene(pane);
stage.setScene(scene);
//here we populate the pane with a Label
//and set that Label again to some value that was passed to this method(foo):
pane.getChildren().add(new Label(foo));
stage.show();
}
这样做没有错。因此,在调用 initialize() 后从 fxml 创建的某些 class 中设置数据没有任何问题。是的,在这种情况下,您不会在 initialize() 中填充,而是从工厂外部填充 - 但那又怎样?
有时我需要在创建对话框后不时地(重新)设置值。所以我为此创建了一个方法。有了那个方法我用它来填充它:
public class DialogController implements Initializable {
@FXML
private AnchorPane dialog;
@FXML
private Label lb_size;
private Setting settings = null;
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {}
public void setSettings(Settings settings, int size) {
this.settings = settings;
this.lb_size.setText("" + size);
}
}
然后我构造它:
public DialogController createDialog(Settings settings, int size){
final FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource("/fxml/afxm.fxm"));
try {
final Stage stage = new Stage();
stage.setScene(new Scene(loader.load()));
final DialogController controller = loader.getController();
controller.setSettings(settings,size);
stage.show();
return controller;
} catch (IOException ex) {
throw new InternalApplicationError("Resource missing", ex);
}
}
现在,每当我需要将设置设置为其他内容时,我都会调用:
controller.setSettings(settings,size);
如果有约束 rg,这当然会失败。该设置可能永远不会为空。通常,如果您 can/want 重新分配值,您无论如何都需要处理这种情况,因此您的 class 应该能够处理使用 settings=null 构建,因为如果您重新设置它可能会发生这种情况。所以你必须在某个地方检查它并确保你没有空指针。大小字段也是如此 - 如果在显示之前未设置它,它将显示默认值 - 但这可能正是你想要的。
有时(视情况而定)我觉得一个单独的工厂是一个不必要的附加 class 而我更想把东西放在一起 class.
为此我有一个简单的基础class:
public class FXMLStage extends Stage implements Initializable {
protected URL url = null;
protected ResourceBundle resourceBundle = null;
@SuppressWarnings("LeakingThisInConstructor")
public FXMLStage(String filename) {
final FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
try {
loader.setControllerFactory(p -> this);
this.setScene(new Scene(loader.load()));
} catch (IOException ex) {
throw new InternalApplicationError("Resource missing", ex);
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
this.url = url;
this.resourceBundle = rb;
}
}
此处初始化仅记住资源包和 url,以便稍后使用。没有别的。
我用 loader.setControllerFactory(p->this) 而不是 loader.setController(this) 设置控制器,原因只有一个:我可以 create/update Java 代码自动为控制器。如果在 fxml 中设置了控制器,IDE 能够从 fxml 自动从控制器中 create/update 字段。如果在 fxml 中设置了控制器,则无法在代码中明确设置。因此,为了方便起见,这更像是一种解决方法。
如果不是这样的话,我宁愿使用 loader.setController(this) 来简单地设置控制器;
此外,我不检查在 p: "loader.setControllerFactory(p -> this);" 中传递的 class - 你可能想要这样做,因为如果 fxml 与控制器不匹配,它当然会失败(错误 class)。但我宁愿希望它在出现问题时失败(控制器的 fxml 错误)而不是默默地继续。因此,一条错误消息告诉我我使用了错误的控制器对我来说是可以接受的。
更重要的是:如果你有嵌套控制器,它也会失败 - 在这种情况下你当然想检查 class 和 return 适当的控制器 - 在这种情况下我宁愿使用一个真正的工厂。
现在从那个基础class我得到一个特定的控制器class:
public class SampleDialog extends FXMLStage {
@FXML
private AnchorPane dialog;
@FXML
private Label lb_size;
//....
//some additional fields to initialize...
private final Session session;
public SampleDialog(Session session, int size) {
super("/fxml/SampleDialog.fxml");
//initialize aditional fields:
this.session=session;
lb_size.setText("" + size);
}
}
所以我可以直接在构造函数中进行初始化 - 我认为这是初始化 class 字段的好地方。所以它根本不会覆盖 initialize() 。
注意传递给构造函数的 "Session" 对象(不管它是什么数据 - 您将拥有您的数据类型)。它作为引用存储在对话框中。因此,无论外部数据发生什么变化,都会直接反映在对话框中。
例如,如果它是一个 Observable,您可以将对话框的元素绑定到它 - 这将始终反映该数据的状态。
如果它是一个 ObservableList,您可以用它在对话框中填充一个 ListView,并且每当 ObservableList 发生更改时,ListView 都会反映列表的状态。与 TableView 相同(例如,我从创建的 HashMap 和其他地方的 populated/updated 填充 TableView。)。
所以模型、视图和控制器的分离成为可能。
只记住 initialize() 的特殊用途。这是施工过程的一部分!因此,如果您覆盖它,则在调用它时可能并非所有字段都已初始化,因此如果您尝试使用这些未初始化的字段之一,它可能会失败。这就是 inizialize() 方法的全部内容:初始化未初始化的字段及其名称应该给你公平的警告。
现在我想使用它:
SampleDialog dialog = new SampleDialog(session,5);
dialog.show();
或者如果我不需要对象:
new SampleDialog(session,5).show();
最后的评论:我没有你的控制器,所以我无法创建现场的例子。我使用了一个舞台,因为它很容易重现,但使用 Tabs 和 TableViews 并没有根本上的不同。此外,我没有尝试为您提供所有类型的方法 - 您在链接的问题中有它们。我试图给出一些例子,说明不同的方法和场景在现实世界的应用程序中可能看起来如何,以及示例中可能发生的事情——希望引发一些关于正在发生的事情的想法,并表明存在一个权衡不止两种方式。祝你好运!
我想问一下程序结构
I have ControllerStatistic
与 FXMLStatistic
相关,我在其中定义 TabPane
.
在 initialize
在 ControllerStatistic
我每个月都添加标签。每个选项卡都包含 FXMLTableViewMonthly
和 ControllerMonthly
。在 ControllerMonthly
中,我想用每个月的每一天的行填充 table。我有来自静态字段的月份信息:
private static int countControllers = 0;
public ControllerUdostepnianieNaZewnatrz() {
countControllers++;
monthNumber = countControllers;
}
我在 initialize
中填充 table。
这可行,但我认为这不是正确的方法。
我想将 ControllerStatistic
中的月份参数传递给 ControllerMonthly
。
我在这里看到 2 个选项:
在 ControllerStatistic
中我从加载器获取控制器并设置月份,然后在 ControllerMonthly
中我无法在 initialize
中填充(月份字段为空)所以我需要在 ControllerStatistic
设置月份字段后。
我还可以从 FXML 中删除 fx:controller
并按照@jewelsea Passing Parameters JavaFX FXML 中所述在代码中构建一个新控制器(他提到他不喜欢该解决方案)。然后我想我可以填充 ControllerMonthly
in initialize
.
我选择使用第二种方法。首先对我来说很糟糕(在设置月份后填充 - 解决方案看起来会导致很多错误)。
如何操作?
您还可以为 FXMLLoader
重新定义 ControllerFactory
。类似于:
loader.setControllerFactory((Class clazz) -> {
if (clazz.isAssignableFrom(SomeClass.class)) {
return new SomeClass(getMonthNumber());
} else {
return clazz.newInstance();
}
});
嗯,没有一般的好坏之分。这取决于您的 usecase/design 和品味。
让我们先看看其他 FX - 没有 fxml 的元素,以及如何填充它们,以走上正确的轨道。以 AnchorPane 为例。首先创建它,创建后用其他元素填充它。当你完成后,你展示了整个事情。您不要覆盖 AnchorPane 中的某些 initialize() 方法:
public void createAStage(String foo){
AnchorPane pane = new AnchorPane();
Stage stage = new Stage();
Scene scene = new Scene(pane);
stage.setScene(scene);
//here we populate the pane with a Label
//and set that Label again to some value that was passed to this method(foo):
pane.getChildren().add(new Label(foo));
stage.show();
}
这样做没有错。因此,在调用 initialize() 后从 fxml 创建的某些 class 中设置数据没有任何问题。是的,在这种情况下,您不会在 initialize() 中填充,而是从工厂外部填充 - 但那又怎样?
有时我需要在创建对话框后不时地(重新)设置值。所以我为此创建了一个方法。有了那个方法我用它来填充它:
public class DialogController implements Initializable {
@FXML
private AnchorPane dialog;
@FXML
private Label lb_size;
private Setting settings = null;
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {}
public void setSettings(Settings settings, int size) {
this.settings = settings;
this.lb_size.setText("" + size);
}
}
然后我构造它:
public DialogController createDialog(Settings settings, int size){
final FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource("/fxml/afxm.fxm"));
try {
final Stage stage = new Stage();
stage.setScene(new Scene(loader.load()));
final DialogController controller = loader.getController();
controller.setSettings(settings,size);
stage.show();
return controller;
} catch (IOException ex) {
throw new InternalApplicationError("Resource missing", ex);
}
}
现在,每当我需要将设置设置为其他内容时,我都会调用:
controller.setSettings(settings,size);
如果有约束 rg,这当然会失败。该设置可能永远不会为空。通常,如果您 can/want 重新分配值,您无论如何都需要处理这种情况,因此您的 class 应该能够处理使用 settings=null 构建,因为如果您重新设置它可能会发生这种情况。所以你必须在某个地方检查它并确保你没有空指针。大小字段也是如此 - 如果在显示之前未设置它,它将显示默认值 - 但这可能正是你想要的。
有时(视情况而定)我觉得一个单独的工厂是一个不必要的附加 class 而我更想把东西放在一起 class.
为此我有一个简单的基础class:
public class FXMLStage extends Stage implements Initializable {
protected URL url = null;
protected ResourceBundle resourceBundle = null;
@SuppressWarnings("LeakingThisInConstructor")
public FXMLStage(String filename) {
final FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
try {
loader.setControllerFactory(p -> this);
this.setScene(new Scene(loader.load()));
} catch (IOException ex) {
throw new InternalApplicationError("Resource missing", ex);
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
this.url = url;
this.resourceBundle = rb;
}
}
此处初始化仅记住资源包和 url,以便稍后使用。没有别的。
我用 loader.setControllerFactory(p->this) 而不是 loader.setController(this) 设置控制器,原因只有一个:我可以 create/update Java 代码自动为控制器。如果在 fxml 中设置了控制器,IDE 能够从 fxml 自动从控制器中 create/update 字段。如果在 fxml 中设置了控制器,则无法在代码中明确设置。因此,为了方便起见,这更像是一种解决方法。
如果不是这样的话,我宁愿使用 loader.setController(this) 来简单地设置控制器;
此外,我不检查在 p: "loader.setControllerFactory(p -> this);" 中传递的 class - 你可能想要这样做,因为如果 fxml 与控制器不匹配,它当然会失败(错误 class)。但我宁愿希望它在出现问题时失败(控制器的 fxml 错误)而不是默默地继续。因此,一条错误消息告诉我我使用了错误的控制器对我来说是可以接受的。 更重要的是:如果你有嵌套控制器,它也会失败 - 在这种情况下你当然想检查 class 和 return 适当的控制器 - 在这种情况下我宁愿使用一个真正的工厂。
现在从那个基础class我得到一个特定的控制器class:
public class SampleDialog extends FXMLStage {
@FXML
private AnchorPane dialog;
@FXML
private Label lb_size;
//....
//some additional fields to initialize...
private final Session session;
public SampleDialog(Session session, int size) {
super("/fxml/SampleDialog.fxml");
//initialize aditional fields:
this.session=session;
lb_size.setText("" + size);
}
}
所以我可以直接在构造函数中进行初始化 - 我认为这是初始化 class 字段的好地方。所以它根本不会覆盖 initialize() 。
注意传递给构造函数的 "Session" 对象(不管它是什么数据 - 您将拥有您的数据类型)。它作为引用存储在对话框中。因此,无论外部数据发生什么变化,都会直接反映在对话框中。
例如,如果它是一个 Observable,您可以将对话框的元素绑定到它 - 这将始终反映该数据的状态。
如果它是一个 ObservableList,您可以用它在对话框中填充一个 ListView,并且每当 ObservableList 发生更改时,ListView 都会反映列表的状态。与 TableView 相同(例如,我从创建的 HashMap 和其他地方的 populated/updated 填充 TableView。)。
所以模型、视图和控制器的分离成为可能。
只记住 initialize() 的特殊用途。这是施工过程的一部分!因此,如果您覆盖它,则在调用它时可能并非所有字段都已初始化,因此如果您尝试使用这些未初始化的字段之一,它可能会失败。这就是 inizialize() 方法的全部内容:初始化未初始化的字段及其名称应该给你公平的警告。
现在我想使用它:
SampleDialog dialog = new SampleDialog(session,5);
dialog.show();
或者如果我不需要对象:
new SampleDialog(session,5).show();
最后的评论:我没有你的控制器,所以我无法创建现场的例子。我使用了一个舞台,因为它很容易重现,但使用 Tabs 和 TableViews 并没有根本上的不同。此外,我没有尝试为您提供所有类型的方法 - 您在链接的问题中有它们。我试图给出一些例子,说明不同的方法和场景在现实世界的应用程序中可能看起来如何,以及示例中可能发生的事情——希望引发一些关于正在发生的事情的想法,并表明存在一个权衡不止两种方式。祝你好运!