栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

是否可以重新加载相同的FXML / Controller实例?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

是否可以重新加载相同的FXML / Controller实例?

我基本上可以通过三种方式执行此操作:

定义一个代表数据(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注入的规则是,将包含的FMXL文件的根目录注入指定的fx:id(例如userInfo)
,并将包含的那些文件的控制器(“嵌套控制器”)注入到名称为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); }        }    }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/464175.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号