我想问一下程序结构。
I have ControllerStatistic
与FXMLStatistic
有关,我在其中定义了TabPane
。
在initialize
的ControllerStatistic
,我每个月都会添加标签。每个标签都包含FXMLTableViewMonthly
和ControllerMonthly
。在ControllerMonthly
我想用每天的行填充表格。我有来自静态字段的月份信息:
private static int countControllers = 0;
public ControllerUdostepnianieNaZewnatrz() {
countControllers++;
monthNumber = countControllers;
}
我在initialize
填写表格。
这有效,但我认为这不是正确的方法。
我想将ControllerStatistic
中的月份参数传递给ControllerMonthly
。
我在这里看到2个选项:
在ControllerStatistic
我从加载器得到控制器并设置月,然后在ControllerMonthly
我不能填充initialize
(月场为空)所以我需要在设置月场后填充ControllerStatistic
。
我也可以从FXML中删除fx:controller
,并在@jewelsea的Passing Parameters JavaFX FXML中描述的代码中构建一个新的控制器(他提到他不喜欢那个解决方案)。然后我想我可以在ControllerMonthly
的initialize
居住。
我选择使用第二种方法。首先看起来非常糟糕(在设置月份之后填充 - 解决方案看起来会导致很多错误)。
这该怎么做?
那么没有一般的坏或好。这取决于您的用例/设计和品味。
让我们先看看其他没有fxml的FX - 元素,以及如何填充它们,以便走上正确的轨道。以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创建的某些类中设置数据没有任何问题。是的,在这种情况下,你不会在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,这当然会失败。该设置可能永远不会为空。通常,如果您可以/想要重新设置值,则无论如何都需要处理该情况,因此您的类应该能够使用settings = null进行处理,因为如果您重新设置它可能会发生。所以你必须在某处检查它并确保你没有得到nullpointer。同样适用于大小字段 - 如果之前没有设置它会显示默认值 - 但这可能是你想要的非常好。
有时(取决于)我觉得一个单独的工厂是一个不必要的额外课程,而是希望在一个课程中将所有东西放在一起。
为此,我有一个简单的基类:
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;
}
}
这里初始化只记住resourcebundle和url,以便我以后可以使用它。没有其他的。
我设置控制器与loader.setControllerFactory(p-> this)而不是loader.setController(this)有一个原因:我可以自动创建/更新控制器的Java代码。如果在fxml中设置了cotroller,则IDE可以从fxml自动创建/更新控制器中的字段。如果在fxml中设置了控制器,则无法在代码中明确地设置它。所以这更方便我的解决方法。
如果不是因为我更喜欢用loader.setController设置控制器(这个);
另外我不检查在p中传递的类:“loader.setControllerFactory(p - > this);” - 您可能希望这样做,因为如果fxml与控制器不匹配(错误的类),它将失败。但我宁愿让它失败,如果出现问题(控制器的错误fxml)而是默默地继续。因此,错误消息告诉我使用错误的控制器对我来说是可以接受的。更多:如果您有嵌套控制器,它也会失败 - 在这种情况下,您当然想要检查类并返回适当的控制器 - 而在这种情况下,imo宁可使用真正的工厂。
现在从该基类我派生出一个特定的控制器类:
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);
}
}
所以我可以直接在构造函数中进行初始化 - 我认为这是初始化类的字段的好地方。所以它根本不会覆盖initialize()。
注意传递给构造函数的“Session”对象(无论它可能是什么数据 - 你将拥有你的数据类型)。它作为参考存储在对话框中。因此,来自外部的数据发生的任何变化都会直接反映在对话框中。
例如,如果它是一个Observable,你可以将对话框的元素绑定到它 - 这将总是反映该数据的状态。 如果它是一个ObservableList,你可以在它的对话框中填充ListView,每当更改ObservableList时,ListView将反映列表的状态。对于TableView也是如此(例如,我在HashMaps中填充了在其他地方创建并填充/更新的TableViews。)。因此,模型,视图和控制器的分离成为可能。
只记住initialize()的特殊目的。这是施工过程的一部分!因此,如果您覆盖它,并非所有字段在调用时都可能已初始化,因此如果您尝试使用其中一个未初始化的字段,它可能会失败。那就是inizialize()方法的全部内容:初始化未初始化的字段及其名称应该给你公平的警告。
现在我想用它:
SampleDialog dialog = new SampleDialog(session,5);
dialog.show();
或者,如果我不需要该对象:
new SampleDialog(session,5).show();
最后的评论:我没有你的控制器,所以我不打算创建现场的例子。我使用舞台因为它很容易重现,但使用Tabs和TableViews并不是完全不同的。此外,我没有试图给你所有的方法 - 你在你的链接问题中有它们。我试着给出一些例子,说明在现实世界的应用程序中可能会出现不同的aproaches和场景,以及示例中可能会发生什么 - 希望触发一些正在发生的事情,并表明存在权衡不止两种方式。祝好运!
你也可以为你的ControllerFactory
重新定义FXMLLoader
。就像是:
loader.setControllerFactory((Class clazz) -> {
if (clazz.isAssignableFrom(SomeClass.class)) {
return new SomeClass(getMonthNumber());
} else {
return clazz.newInstance();
}
});