Ну нет вообще плохого или хорошего.Это зависит от вашего варианта использования / дизайна и вкуса.
Давайте сначала посмотрим на другие FX-элементы без fxml и то, как вы их заполнили, чтобы встать на правильный путь.Возьмите AnchorPane например.Сначала вы создаете его, а после его создания вы заполняете его дополнительными элементами.Когда вы закончите, вы показываете все это.Вы не переписываете какой-либо метод initialize () в AnchorPane:
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();
}
В этом нет ничего плохого.И поэтому нет ничего плохого в установке данных в некотором классе, который был создан из fxml после вызова initialize ().И да, в этом случае вы заполняете не 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, как это может произойти, если вы его повторно определили.Так что вы должны где-то проверить это и убедиться, что у вас нет нулевого указателя.То же самое верно для поля размера - если оно не было установлено до его показа, оно будет показывать значение по умолчанию - но это может быть очень хорошо, что вы хотите.
Иногда (в зависимости) я чувствую, что отдельная фабрика не нужнадополнительный класс и скорее хочу, чтобы все было вместе в одном классе.
Для этого у меня есть простой базовый класс:
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) по одной единственной причине: я могу автоматически создавать / обновлять код Java для контроллера.Среда IDE может создавать / обновлять поля в контроллере автоматически из fxml, если в fxml установлен шаблон.И если в fxml установлен контроллер, вы не можете установить его явно в коде.Так что для моего удобства это скорее обходной путь.
Если бы не это, я бы предпочел установить простой контроллер с помощью loader.setController (this);
Также я не проверяю класс, который передается в p: "loader.setControllerFactory (p -> this);"- вы можете захотеть сделать это, поскольку, конечно, произойдет сбой, если fxml не соответствует контроллеру (неправильный класс).Но я скорее хочу, чтобы он потерпел неудачу, если что-то не так (неправильный fxml для контроллера), вместо этого молча продолжаетсяТаким образом, сообщение об ошибке, говорящее мне, что я использую неправильный контроллер, приемлемо для меня.Более того: он также потерпит неудачу, если у вас есть вложенные контроллеры - в этом случае вы наверняка захотите проверить класс и вернуть соответствующий контроллер - и в этом случае я скорее использую реальную фабрику.
Теперь из этого базового классаЯ получаю определенный класс контроллера:
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", который передается конструктору (не имеет значения, какие это могут быть данные - у вас будут данные вашего типа).Он сохраняется как ссылка в диалоге.Поэтому любые изменения, происходящие с данными извне, напрямую отражаются в диалоговом окне.
Если бы это было, например, наблюдаемое, вы могли бы привязать к нему элементы своего диалога, что всегда отражало бы состояние этих данных.
Если бы это был ObservableList, вы могли бы заполнить ListView в диалоге с ним, и всякий раз, когда этот ObservableList изменяется, ListView будет отражать состояние списка. То же самое для TableView (например, я заполняю TableViews из HashMaps, которые создаются и заполняются / обновляются где-то еще).
Таким образом, становится возможным разделение модели, вида и контроллера.
Помните только о специальной цели initialize (). Это часть процесса строительства! Поэтому, если вы перезаписываете его, не все поля могли быть инициализированы еще при его вызове, и поэтому он может потерпеть неудачу, если вы попытаетесь использовать одно из этих неинициализированных полей. Вот в чем заключается метод inizialize (): инициализация неинициализированных полей и их имени должны дать вам справедливое предупреждение.
Теперь я хочу использовать это:
SampleDialog dialog = new SampleDialog(session,5);
dialog.show();
Или, если мне не нужен объект:
new SampleDialog(session,5).show();
Последнее замечание: у меня не было вашего контроллера, поэтому я не могу создавать примеры, которые можно найти. Я использовал Stage, потому что его просто воспроизвести, но использование Tabs и TableViews не сильно отличается. Также я не пытался дать вам все виды подходов - они есть в вашем связанном вопросе. Я попытался привести несколько примеров того, как различные подходы и сценарии могут выглядеть в приложении реального мира и что может произойти в примерах - в надежде вызвать некоторое представление о том, что происходит, и показать, что существует компромисс между намного больше, чем два пути. Удачи!