目标:实施标准的“设置” GUI窗口。类别位于
ListView左侧,而相应选项位于Pane右侧。 (请忽略具有重复类别的明显错误;仍在处理)
在此处输入图片说明
我有一个用于整体“设置”窗口的主窗口,其中包含
ListView带有所有类别的设置。窗口的右侧
具有AnchorPane,
当从列表中选择一个类别时,用于为每个类别加载单独的FXML文件。
当用户选择类别时,我需要他们能够编辑
右侧的设置,切换到另一个类别并进行更多更改。但是,如果它们
返回到第一类,则在那里进行的更改仍然存在。
我的明显问题是,每次用户更改类别时,都会FXMLLoader
重新加载FXML文件和控制器,将所有控件重置为其
默认值。
因此,可以重用已经加载和更改的FXML文件吗?
研究:
我发现似乎可以解决该问题的唯一答案是如何在
不重新加载FXML
文件的情况下切换javafx应用程序控制器?。提到了将
Singleton用于FXML控制器,但是没有解决
每次重新加载FXML文件本身的问题。
如果有人可以指出这种“
设置”菜单的基本示例,我将非常高兴。
我基本上可以通过三种方式执行此操作:
定义一个代表数据(Settings)的模型,并为其创建一个实例。每次都重新加载FXML文件,并将单个实例传递给控制器。将UI中的数据与模型中的数据绑定。这样,当您重新加载FXML时,它将使用相同的数据进行更新。(这是我的首选。)
一次创建控制器。每次重新加载FXML文件,每次设置相同的控制器。让该initialize()方法从本地存储的字段或模型更新UI。@FXML重新加载FXML文件时,-annotated字段将被替换,该initialize()方法将被调用,并使用现有数据更新新控件。(这感觉有些虚假。从道德上讲,任何调用的方法initialize()都只能执行一次。但是,这是完全可行的。)
加载每个FXML文件一次,并缓存UI(可能还包含控制器)。然后,当用户在列表视图中选择某项时,仅显示已加载的视图。这可能是最简单的方法,但是由于您始终将所有视图都保留在内存中,因此会在内存上花费更多。
假设您有一个模型,看起来可能像这样:
public class Settings {
private final UserInfo userInfo ;
private final Preferences prefs ;
private final Appearance appearance ;
public Settings(UserInfo userInfo, Preferences prefs, Appearance appearance) {
this.userInfo = userInfo ;
this.prefs = prefs ;
this.appearance = appearance ;
}
public Settings() {
this(new UserInfo(), new Preferences(), new Appearance());
}
public UserInfo getUserInfo() {
return userInfo ;
}
public Preferences getPreferences() {
return prefs ;
}
public Appearance getAppearance() {
return appearance ;
}
}
and
public class UserInfo {
private final StringProperty name = new SimpleStringProperty() ;
private final StringProperty department = new SimpleStringProperty() ;
// etc...
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
// etc...
}
(and similarly for Preferences
, Appearance
, etc.)
Now you define controllers for you individual screens that use a model, e.g.
public class UserInfoController {
private final UserInfo userInfo ;
@FXML
private TextField name ;
@FXML
private ComboBox<String> department ;
public UserInfoController(UserInfo userInfo) {
this.userInfo = userInfo ;
}
public void initialize() {
name.textProperty().bindBidirectional(userInfo.nameProperty());
department.valueProperty().bindBidirectional(userInfo.departmentProperty());
}
}
and then you main controller looks like:
public class MainController {
@FXML
private BorderPane root ;
@FXML
private ListView<String> selector ;
private Settings settings = new Settings() ; // or pass in from somewhere else..
public void initialize() {
selector.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if ("User Information".equals(newSelection)) {
loadScreen("UserInfo.fxml", new UserInfoController(settings.getUserInfo()));
} else if ("Preferences".equals(newSelection)) {
loadScreen("Preferences.fxml", new PreferencesController(settings.getPreferences()));
} else if ("Appearance".equals(newSelection)) {
loadScreen("Appearance.fxml", new AppearanceController(settings.getAppearance()));
} else {
root.setCenter(null);
}
}
private void loadScreen(String resource, Object controller) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(resource));
loader.setController(controller);
root.setCenter(loader.load());
} catch (IOException exc) {
exc.printStackTrace();
root.setCenter(null);
}
}
}
(显然,您可以通过定义一个
简单的视图类来封装列表视图的处理程序,该类封装了资源名称,显示名称和
控制器的工厂,并用它填充列表视图,而不用
打开字符串。)
应当指出,由于要设置在控制器FXMLLoader中的代码,
UserInfo.fxml,Preferences.fxml并且Appearance.fxml应该不会有一个
fx:controller定义的属性。
第二种选择只是对此的适度重构。
一次创建控制器并对其进行引用。请注意,如果需要,您可以摆脱
此版本中的模型,因为控制器具有数据,因此您可以
参考它们。所以这看起来像
public class UserInfoController {
@FXML
private TextField name ;
@FXML
private ComboBox<String> department ;
private final StringProperty nameProp = new SimpleStringProperty();
private final ObjectProperty<String> departmentProp = new SimpleObjectProperty();
public StringProperty nameProperty() {
return nameProp;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
public ObjectProperty<String> departmentProperty() {
return departmentProp ;
}
public final String getDepartment() {
return departmentProperty().get();
}
public final void setDepartment(String department) {
departmentProperty().set(department);
}
public void initialize() {
// initialize controls with data currently in properties,
// and ensure changes to controls are written back to properties:
name.textProperty().bindBidirectional(nameProp);
department.valueProperty().bindBidirectional(departmentProp);
}
}
and then
public class MainController {
@FXML
private BorderPane root ;
@FXML
private ListView<String> selector ;
private UserInfoController userInfoController = new UserInfoController();
private PreferencesController preferencesController = new PreferencesController();
private AppearanceController appearanceController = new AppearanceController();
public void initialize() {
// initialize controllers with data if necessary...
selector.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
selector.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if ("User Information".equals(newSelection)) {
loadScreen("UserInfo.fxml", userInfoController);
} else if ("Preferences".equals(newSelection)) {
loadScreen("Preferences.fxml", preferencesController);
} else if ("Appearance".equals(newSelection)) {
loadScreen("Appearance.fxml", appearanceController);
} else {
root.setCenter(null);
}
}
}
private void loadScreen(String resource, Object controller) {
// as before...
}
}
之所以可行,是因为在重新加载FXML文件时不会创建新的控制器,并且控制器中的initialize方法会
使用已经存在的数据来更新控件。(请注意以哪种方式bindBidirectional
调用方法。)
第三个选项可以在主控制器中或在主fxml文件中实现。要在控制器中实现它,基本上
public class MainController {
@FXML
private BorderPane root ;
@FXML
private ListView<String> selector ;
private Parent userInfo ;
private Parent prefs;
private Parent appearance;
// need controllers to get data later...
private UserInfoController userInfoController ;
private PreferencesController prefsController ;
private AppearanceController appearanceController ;
public void initialize() throws IOException {
FXMLLoader userInfoLoader = new FXMLLoader(getClass().getResource("userInfo.fxml));
userInfo = userInfoLoader.load();
userInfoController = userInfoLoader.getController();
FXMLLoader prefsLoader = new FXMLLoader(getClass().getResource("preferences.fxml));
prefs = prefsLoader.load();
prefsController = prefsLoader.getController();
FXMLLoader appearanceLoader = new FXMLLoader(getClass().getResource("appearance.fxml));
appearance = appearanceLoader.load();
appearanceController = appearanceLoader.getController();
// configure controllers with data if needed...
selector.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if ("User Information".equals(newSelection)) {
root.setCenter(userInfo);
} else if ("Preferences".equals(newSelection)) {
root.setCenter(prefs);
} else if ("Appearance".equals(newSelection)) {
root.setCenter(prefs);
} else {
root.setCenter(null);
}
}
}
}
请注意,此处您将恢复为fx:controllerFXML文件中的常规属性。
这将起作用,因为您仅加载一次FXML文件,因此视图仅保留其所有状态。
如果要通过这种方法在FXML中定义视图,则可以:
主fxml文件:
<!-- imports etc omitted -->
<BorderPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.MainController">
<left>
<ListView fx:id="selector" />
</left>
<fx:define>
<fx:include fx:id="userInfo" source="UserInfo.fxml" >
</fx:define>
<fx:define>
<fx:include fx:id="prefs" source="Preferences.fxml" >
</fx:define>
<fx:define>
<fx:include fx:id="appearance" source="Appearance.fxml" >
</fx:define>
</BorderPane>
FXML注入的规则
,并将包含的那些文件的控制器(“嵌套控制器”)注入到名称为when的字段”Controller”中。的fx:id(例如,到userInfoController)。因此,这的主控制器现在看起来像
public class MainController {
@FXML
private BorderPane root ;
@FXML
private ListView<String> selector ;
@FXML
private Parent userInfo ;
@FXML
private Parent prefs;
@FXML
private Parent appearance;
// need controllers to get data later...
@FXML
private UserInfoController userInfoController ;
@FXML
private PreferencesController prefsController ;
@FXML
private AppearanceController appearanceController ;
public void initialize() {
// configure controllers with data if needed...
selector.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if ("User Information".equals(newSelection)) {
root.setCenter(userInfo);
} else if ("Preferences".equals(newSelection)) {
root.setCenter(prefs);
} else if ("Appearance".equals(newSelection)) {
root.setCenter(prefs);
} else {
root.setCenter(null);
}
}
}
}
我有一个用于总体设置窗口的主窗口,其中包含一个和所有设置类别。窗口的右侧有一个,用于在从列表中选择每个类别时为每个类别加载单独的FXML文件。 当一个用户选择一个类别,我需要他们能够编辑右边的设置,切换到另一个类别,并作出更多的改变。然而,如果他们回到第一类,那里所做的改变会持续下去。 我的明显问题是,每次用户更改类别时,都会重新加载FXML文件和控制器,将其中的所有控件重置为它们的默认值。 研究
我已经为一个ScreenController类(一个由每个屏幕的单个控制器类扩展的类)创建了一个解决方案,它将处理我的应用程序中的基线屏幕层次结构。 在我的类中,我使用一个函数将另一个FXML文件的内容(加载)添加到当前控制器的当前主播上。 我的问题是: 1) 加载新的FXML时,FXML使用的类(或者更确切地说,特定的控制器)是否也被实例化/加载? 2) 执行此操作时,如果新FXMl的类被实例化
问题内容: 我的.routing.ts文件中有这个 我的文件检查id参数并相应地加载数据。在路由器的早期版本中,如果我从/ page / 4转到/ page / 25,则该页面将“重新加载”并且组件将更新。 现在,当我尝试导航到/ page / X时,其中X是id,它只会第一次加载,然后url会更改,但是组件不会再次“重新加载”。 是否需要传递某些内容以强制重新加载组件并调用ngOnInit事件?
问题内容: 设法获得logstash(1.3.1)以将数据发送到elasticsearch(0.9.5)。 我的logstash conf文件设置是 数据存储在ES中的索引logstash-2013.12.xx下 但是,如果我重新启动logstash,请说第二天-将相同的数据重新加载到新索引中。即使我再次重新启动,索引中的文档计数也会加倍。 好像logstash重新读取数据,ES也在复制文档。 有
问题内容: 我正在建立一个新的由AJAX驱动的网站,其中包含不同的部分。每个部分都需要一组新的Javascript函数才能运行。我宁愿不要一开始就加载每个脚本,因为可能会有很多脚本。 有没有一种方法可以使用AJAX加载新脚本并删除旧脚本(以确保类似的变量名或函数签名不存在兼容性问题)。 谢谢 编辑 -jQuery很好,它不必是老式的Javascript 问题答案: 三件事: 1)是,您可以加载新脚
我已经创建了一个按钮数组(),我想为数组中的所有按钮添加相同的操作侦听器,而不是逐个添加它们。 想象一下,每次我点击屏幕上显示的一个按钮,它就会在数组中打印出该按钮的索引。