我想问一下程序的结构。
I have ControllerStatistic
与FXMLStatistic
相关,其中我定义了TabPane
。
在initialize
的{{1}}中,我为每个月添加标签。每个标签都包含ControllerStatistic
和FXMLTableViewMonthly
。在ControllerMonthly
中,我想用每月每个月的行填充表。我有来自静态字段的月份信息:
ControllerMonthly
我在private static int countControllers = 0;
public ControllerUdostepnianieNaZewnatrz() {
countControllers++;
monthNumber = countControllers;
}
中填充表格。
这行得通,但我认为这不是正确的方法。
我想将initialize
中的month参数传递给ControllerStatistic
。
我在这里看到2个选项:
在ControllerMonthly
中,我从加载器获取了控制器并设置了月,然后在ControllerStatistic
中,我无法在ControllerMonthly
中填充(month字段为空),所以我需要在{{1 }},然后设置月份字段。
我还可以从FXML中删除initialize
并按照@jewelsea的Passing Parameters JavaFX FXML中所述的代码构造新的控制器(他提到他不喜欢这种解决方案)。然后,我认为可以在ControllerStatistic
的{{1}}中填充。
我选择使用第二种方法。首先对我来说非常糟糕(在设置月份后填充-解决方案看起来会引起很多错误)。
该怎么做?
答案 0 :(得分:1)
您也可以将ControllerFactory
重新定义为FXMLLoader
。像这样:
loader.setControllerFactory((Class clazz) -> {
if (clazz.isAssignableFrom(SomeClass.class)) {
return new SomeClass(getMonthNumber());
} else {
return clazz.newInstance();
}
});
答案 1 :(得分:1)
没有一般的坏处或好处。这取决于您的用例/设计和口味。
让我们看看其他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创建的某个类中设置数据没有任何问题。是的,在那种情况下,您不需要在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进行构造,因为如果您租赁它,可能会发生这种情况。因此,您必须在某处进行检查并确保没有空指针。相同的适用于size字段-如果未在显示之前设置它,它将显示默认值-但这可能是您想要的。
有时(取决于)我觉得一个单独的工厂是不必要的附加类,而是想将所有东西放在一个类中。
为此,我有一个简单的基类:
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(this);设置控制器简单。
我也不检查在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()。
请注意传递给构造函数的“会话”对象(无论它是什么数据-您将拥有自己的数据)。它作为参考存储在对话框中。因此,外部数据发生的任何变化都将直接反映在对话框中。
例如,如果它是一个Observable,则可以将对话框的元素绑定到它-始终会反映该数据的状态。
如果它是一个ObservableList,则可以使用它来填充对话框中的ListView,并且每当ObservableList更改时,ListView就会反映列表的状态。与TableView相同(例如,我从HashMaps填充TableViews,这些HashMaps是在其他地方创建和填充/更新的。)。
这样就可以实现模型,视图和控制器的分离。
仅牢记initialize()的特殊用途。这是施工过程的一部分!因此,如果您覆盖它,则在调用它时可能尚未初始化所有字段,因此,如果您尝试使用这些未初始化的字段之一,则可能会失败。那就是inizialize()方法的全部意义:初始化未初始化的字段及其名称应该给您公平的警告。
现在我要使用它:
SampleDialog dialog = new SampleDialog(session,5);
dialog.show();
或者如果我不需要该对象:
new SampleDialog(session,5).show();
最后一句话:我没有您的控制人,所以我不能创建一些例子。我使用了舞台,因为它很容易复制,但是使用Tabs和TableViews并没有什么不同。另外,我也没有尝试提供所有方法-在链接的问题中都包含了这些方法。我试图给出一些示例,说明在现实世界中的应用中不同的方式和场景是什么样的,以及示例中可能发生的情况-希望引发对正在发生的事情的一些了解,并表明存在权衡取舍远远超过两种方式。祝好运!