状态改变时更新 fxml 标签
Update fxml tag when state changes
我想创建某种 'security' fxml 标记,根据某种 SecurityManager class.
的状态禁用/使其子项不可见
我遇到的困难如下。当 SecurityManager class 的状态发生变化时,我希望所有的 securityTags 更新它们可见的 属性。当然,每次调用标记构造函数时,我都可以将所有 SecurityTag 节点添加到静态列表中,并在 SecurityManger class 更改状态时对其进行循环。但是,如果安全标记节点从父节点中删除怎么办?我如何在列表中删除它?或者也许有一个更全面的更好的方法来处理这个问题?
public class SecurityTag extends Pane {
public Security() {
super();
this.setVisible(false);
}
}
public class SecurityManager {
private boolean authorized;
public SecurityManager() {
this.authorized = false;
}
public void login() {
this.authorized = true;
}
public void logout() {
this.authorized = false;
}
public boolean isAuthorized() {
return authorized;
}
}
最直接的方法是将 SecurityManager
中的 authorized
属性 设为 JavaFX 属性:
package org.jamesd.examples.security;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class SecurityManager {
private final ReadOnlyBooleanWrapper authorized ;
public SecurityManager() {
this.authorized = new ReadOnlyBooleanWrapper(false) ;
}
public void login() {
this.authorized.set(true);
}
public void logout() {
this.authorized.set(false);
}
public ReadOnlyBooleanProperty authorizedProperty() {
return authorized.getReadOnlyProperty();
}
public boolean isAuthorized() {
return authorizedProperty().get();
}
}
现在您可以简单地将相关属性绑定到 SecurityManager
的 authorized
属性。根据要绑定的 属性,您可以直接在 FXML 或控制器中执行此操作。您可以通过将 FXMLLoader
的 namespace
中的 SecurityManager
实例放置在 FXML 文件中,并通过将其作为参数传递给控制器使其对控制器可用构造函数,并在 FXMLLoader
.
上手动设置控制器(即不使用 fx:controller
属性)
这是一个示例 FXML 文件。请注意 "Privileged Action" 按钮如何使用
将其可见性绑定到安全管理器
visible = "${securityManager.authorized}"
你也可以
disable = "${ !securityManager.authorized}"
如果您只想禁用它。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets ?>
<?import javafx.scene.layout.BorderPane ?>
<?import javafx.scene.layout.VBox ?>
<?import javafx.scene.control.Button ?>
<?import javafx.scene.control.Label ?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label fx:id = "securityStatus"></Label>
</top>
<center>
<VBox spacing="5" fillWidth="true">
<Button text="Regular Action" maxWidth="Infinity"></Button>
<Button text="Privileged Action" visible = "${securityManager.authorized}" maxWidth="Infinity"></Button>
<padding>
<Insets top="5" left="5" right="5" bottom="5"/>
</padding>
</VBox>
</center>
<left>
<VBox spacing="5" fillWidth="true">
<Button text="login" onAction="#login" maxWidth="Infinity"/>
<Button text="logout" onAction="#logout" maxWidth="Infinity"/>
<padding>
<Insets top="5" left="5" right="5" bottom="5"/>
</padding>
</VBox>
</left>
</BorderPane>
这是一个控制器。使用比 FXML 中更复杂的绑定将标签的文本绑定到安全管理器的状态:
package org.jamesd.examples.security;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class SecurityController {
private final SecurityManager securityManager ;
@FXML
private Label securityStatus ;
public SecurityController(SecurityManager securityManager) {
this.securityManager = securityManager ;
}
public void initialize() {
securityStatus.textProperty().bind(Bindings
.when(securityManager.authorizedProperty())
.then("Logged In")
.otherwise("Logged Out")
);
}
@FXML
private void login() {
securityManager.login();
}
@FXML
private void logout() {
securityManager.logout();
}
}
最后,这是全部组装的方式:
package org.jamesd.examples.security;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SecurityApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
SecurityManager securityManager = new SecurityManager();
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
loader.getNamespace().put("securityManager", securityManager);
loader.setController(new SecurityController(securityManager));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
请注意,此方法避免了 JavaFX 节点(例如 Pane
)的任何不必要的子classing,这可能会导致问题(例如,您可能想要放置现有布局窗格中依赖于安全的节点,这使得使用标准布局变得更加困难。
如果如评论中所建议的那样,您希望 SecurityManager
class 与 JavaFX(一般来说可能是桌面 Java)无关,您可以简单地为使用 JavaFX 属性的 UI 创建一个委托,并安排在更新 "real" 安全管理器时更新它。
例如这里有一个 SecurityManager
实现了 classical "listener" 模式:
package org.jamesd.examples.security;
@FunctionalInterface
public interface AuthorizationListener {
void authorizationChanged(boolean newStatus);
}
和
package org.jamesd.examples.security;
import java.util.ArrayList;
import java.util.List;
public class SecurityManager {
private boolean authorized ;
private final List<AuthorizationListener> listeners ;
public SecurityManager() {
this.listeners = new ArrayList<>();
}
public void login() {
setAuthorized(true);
}
public void logout() {
setAuthorized(false);
}
public void addListener(AuthorizationListener listener) {
listeners.add(listener);
}
public void removeListener(AuthorizationListener listener) {
listeners.remove(listener);
}
public boolean isAuthorized() {
return authorized;
}
private void setAuthorized(boolean authorized) {
if (! this.authorized == authorized) {
this.authorized = authorized ;
listeners.forEach(l -> l.authorizationChanged(authorized));
}
}
}
请注意,这些与任何视图技术完全无关。
现在您可以创建 "UI security manager delegate":
package org.jamesd.examples.security;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class UISecurityDelegate {
private final ReadOnlyBooleanWrapper authorized ;
private final SecurityManager manager ;
public UISecurityDelegate(SecurityManager manager) {
this.manager = manager ;
this.authorized = new ReadOnlyBooleanWrapper(manager.isAuthorized()) ;
manager.addListener(authorized::set);
}
public void login() {
manager.login();
}
public void logout() {
manager.logout();
}
public ReadOnlyBooleanProperty authorizedProperty() {
return authorized.getReadOnlyProperty();
}
public boolean isAuthorized() {
return authorizedProperty().get();
}
}
最后用
更新 UI 代码
package org.jamesd.examples.security;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class SecurityController {
private final UISecurityDelegate securityManager ;
@FXML
private Label securityStatus ;
public SecurityController(UISecurityDelegate securityManager) {
this.securityManager = securityManager ;
}
public void initialize() {
securityStatus.textProperty().bind(Bindings
.when(securityManager.authorizedProperty())
.then("Logged In")
.otherwise("Logged Out")
);
}
@FXML
private void login() {
securityManager.login();
}
@FXML
private void logout() {
securityManager.logout();
}
}
和
package org.jamesd.examples.security;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SecurityApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// probably created by data or service layer, etc:
SecurityManager securityManager = new SecurityManager();
UISecurityDelegate securityDelegate = new UISecurityDelegate(securityManager) ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
loader.getNamespace().put("securityManager", securityDelegate);
loader.setController(new SecurityController(securityDelegate));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
我想创建某种 'security' fxml 标记,根据某种 SecurityManager class.
的状态禁用/使其子项不可见我遇到的困难如下。当 SecurityManager class 的状态发生变化时,我希望所有的 securityTags 更新它们可见的 属性。当然,每次调用标记构造函数时,我都可以将所有 SecurityTag 节点添加到静态列表中,并在 SecurityManger class 更改状态时对其进行循环。但是,如果安全标记节点从父节点中删除怎么办?我如何在列表中删除它?或者也许有一个更全面的更好的方法来处理这个问题?
public class SecurityTag extends Pane {
public Security() {
super();
this.setVisible(false);
}
}
public class SecurityManager {
private boolean authorized;
public SecurityManager() {
this.authorized = false;
}
public void login() {
this.authorized = true;
}
public void logout() {
this.authorized = false;
}
public boolean isAuthorized() {
return authorized;
}
}
最直接的方法是将 SecurityManager
中的 authorized
属性 设为 JavaFX 属性:
package org.jamesd.examples.security;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class SecurityManager {
private final ReadOnlyBooleanWrapper authorized ;
public SecurityManager() {
this.authorized = new ReadOnlyBooleanWrapper(false) ;
}
public void login() {
this.authorized.set(true);
}
public void logout() {
this.authorized.set(false);
}
public ReadOnlyBooleanProperty authorizedProperty() {
return authorized.getReadOnlyProperty();
}
public boolean isAuthorized() {
return authorizedProperty().get();
}
}
现在您可以简单地将相关属性绑定到 SecurityManager
的 authorized
属性。根据要绑定的 属性,您可以直接在 FXML 或控制器中执行此操作。您可以通过将 FXMLLoader
的 namespace
中的 SecurityManager
实例放置在 FXML 文件中,并通过将其作为参数传递给控制器使其对控制器可用构造函数,并在 FXMLLoader
.
fx:controller
属性)
这是一个示例 FXML 文件。请注意 "Privileged Action" 按钮如何使用
将其可见性绑定到安全管理器visible = "${securityManager.authorized}"
你也可以
disable = "${ !securityManager.authorized}"
如果您只想禁用它。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets ?>
<?import javafx.scene.layout.BorderPane ?>
<?import javafx.scene.layout.VBox ?>
<?import javafx.scene.control.Button ?>
<?import javafx.scene.control.Label ?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label fx:id = "securityStatus"></Label>
</top>
<center>
<VBox spacing="5" fillWidth="true">
<Button text="Regular Action" maxWidth="Infinity"></Button>
<Button text="Privileged Action" visible = "${securityManager.authorized}" maxWidth="Infinity"></Button>
<padding>
<Insets top="5" left="5" right="5" bottom="5"/>
</padding>
</VBox>
</center>
<left>
<VBox spacing="5" fillWidth="true">
<Button text="login" onAction="#login" maxWidth="Infinity"/>
<Button text="logout" onAction="#logout" maxWidth="Infinity"/>
<padding>
<Insets top="5" left="5" right="5" bottom="5"/>
</padding>
</VBox>
</left>
</BorderPane>
这是一个控制器。使用比 FXML 中更复杂的绑定将标签的文本绑定到安全管理器的状态:
package org.jamesd.examples.security;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class SecurityController {
private final SecurityManager securityManager ;
@FXML
private Label securityStatus ;
public SecurityController(SecurityManager securityManager) {
this.securityManager = securityManager ;
}
public void initialize() {
securityStatus.textProperty().bind(Bindings
.when(securityManager.authorizedProperty())
.then("Logged In")
.otherwise("Logged Out")
);
}
@FXML
private void login() {
securityManager.login();
}
@FXML
private void logout() {
securityManager.logout();
}
}
最后,这是全部组装的方式:
package org.jamesd.examples.security;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SecurityApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
SecurityManager securityManager = new SecurityManager();
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
loader.getNamespace().put("securityManager", securityManager);
loader.setController(new SecurityController(securityManager));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
请注意,此方法避免了 JavaFX 节点(例如 Pane
)的任何不必要的子classing,这可能会导致问题(例如,您可能想要放置现有布局窗格中依赖于安全的节点,这使得使用标准布局变得更加困难。
如果如评论中所建议的那样,您希望 SecurityManager
class 与 JavaFX(一般来说可能是桌面 Java)无关,您可以简单地为使用 JavaFX 属性的 UI 创建一个委托,并安排在更新 "real" 安全管理器时更新它。
例如这里有一个 SecurityManager
实现了 classical "listener" 模式:
package org.jamesd.examples.security;
@FunctionalInterface
public interface AuthorizationListener {
void authorizationChanged(boolean newStatus);
}
和
package org.jamesd.examples.security;
import java.util.ArrayList;
import java.util.List;
public class SecurityManager {
private boolean authorized ;
private final List<AuthorizationListener> listeners ;
public SecurityManager() {
this.listeners = new ArrayList<>();
}
public void login() {
setAuthorized(true);
}
public void logout() {
setAuthorized(false);
}
public void addListener(AuthorizationListener listener) {
listeners.add(listener);
}
public void removeListener(AuthorizationListener listener) {
listeners.remove(listener);
}
public boolean isAuthorized() {
return authorized;
}
private void setAuthorized(boolean authorized) {
if (! this.authorized == authorized) {
this.authorized = authorized ;
listeners.forEach(l -> l.authorizationChanged(authorized));
}
}
}
请注意,这些与任何视图技术完全无关。
现在您可以创建 "UI security manager delegate":
package org.jamesd.examples.security;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class UISecurityDelegate {
private final ReadOnlyBooleanWrapper authorized ;
private final SecurityManager manager ;
public UISecurityDelegate(SecurityManager manager) {
this.manager = manager ;
this.authorized = new ReadOnlyBooleanWrapper(manager.isAuthorized()) ;
manager.addListener(authorized::set);
}
public void login() {
manager.login();
}
public void logout() {
manager.logout();
}
public ReadOnlyBooleanProperty authorizedProperty() {
return authorized.getReadOnlyProperty();
}
public boolean isAuthorized() {
return authorizedProperty().get();
}
}
最后用
更新 UI 代码package org.jamesd.examples.security;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class SecurityController {
private final UISecurityDelegate securityManager ;
@FXML
private Label securityStatus ;
public SecurityController(UISecurityDelegate securityManager) {
this.securityManager = securityManager ;
}
public void initialize() {
securityStatus.textProperty().bind(Bindings
.when(securityManager.authorizedProperty())
.then("Logged In")
.otherwise("Logged Out")
);
}
@FXML
private void login() {
securityManager.login();
}
@FXML
private void logout() {
securityManager.logout();
}
}
和
package org.jamesd.examples.security;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SecurityApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// probably created by data or service layer, etc:
SecurityManager securityManager = new SecurityManager();
UISecurityDelegate securityDelegate = new UISecurityDelegate(securityManager) ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
loader.getNamespace().put("securityManager", securityDelegate);
loader.setController(new SecurityController(securityDelegate));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}