如果满足条件,有没有办法只在 Javafx 中“渲染”一个组件?我有兴趣做一个具有不同角色的用户界面,如果角色允许,只需添加一个组件,我也想继续使用 FXML。我还没有读过类似的东西。
1 回答
绑定可见性
将组件的visible 属性绑定到BooleanExpression,表示它应该可见的条件。
FXML 可以进行绑定,但请注意,从 JavaFX 2.2 开始,“目前仅支持解析为属性值或页面变量的简单表达式。将来可能会添加对涉及布尔或其他运算符的更复杂表达式的支持。”
如果您未在 FXML 中绑定,则可以使用 JavaFX API 在 Java 代码中进行绑定。
node.visibleProperty().bind(conditionProperty);
如果不希望不可见属性占用布局空间,也请先将托管属性绑定到可见属性。
node.managedProperty().bind(node.visibleProperty());
交替更改更改侦听器中的可见性
请注意,为基于角色的完整系统设置绑定非常复杂,因此对于这样的任务,您最好使用更改侦听器并在 Java 中编码部分逻辑,而不是尝试在 FXML 中完成所有操作。您仍然可以在 FXML 中设计您的 UI,但您可以在控制器中添加一些代码来管理哪些项目对于哪些角色可见。
基于角色的显示示例
这是使用 Java API 的基于角色的解决方案。
该解决方案根据所选角色显示不同的标记图片。
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.*;
enum Role { father, son, mother, daughter, brother, sister }
class RoleManager {
private final Map<Node, List<Role>> nodeRoles = new HashMap<>();
private ObservableList<Role> activeRoles;
public final ListChangeListener<Role> ACTIVE_ROLE_LISTENER = new ListChangeListener<Role>() {
@Override
public void onChanged(Change<? extends Role> c) {
showActiveNodes();
}
};
public void setActiveRoles(ObservableList<Role> activeRoles) {
if (this.activeRoles != null) {
this.activeRoles.removeListener(ACTIVE_ROLE_LISTENER);
}
this.activeRoles = activeRoles;
this.activeRoles.addListener(ACTIVE_ROLE_LISTENER);
}
public void showActiveNodes() {
for (Node node : nodeRoles.keySet()) {
node.setVisible(isActive(node));
}
}
public void assignRole(Node node, Role... roles) {
nodeRoles.put(node, Arrays.asList(roles));
}
private boolean isActive(Node node) {
if (activeRoles == null) {
return false;
}
for (Role role: nodeRoles.get(node)) {
if (activeRoles.contains(role)) {
return true;
}
}
return false;
}
}
public class RoleVisibility extends Application {
private RoleManager roleManager = new RoleManager();
@Override
public void start(Stage stage) {
VBox layout = new VBox(10);
layout.getChildren().setAll(
getRoleChooser(),
createContent()
);
layout.setStyle("-fx-padding: 10px; -fx-background-color: cornsilk;");
roleManager.showActiveNodes();
stage.setTitle("Role Selector");
stage.setScene(new Scene(layout));
stage.show();
}
private Node getRoleChooser() {
ObservableList<Role> activeRoles = FXCollections.observableArrayList();
VBox roleChooser = new VBox(10);
for (final Role role: Role.values()) {
CheckBox checkBox = new CheckBox(role.toString());
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasSelected, Boolean isSelected) {
if (isSelected) {
activeRoles.add(role);
} else {
activeRoles.remove(role);
}
}
});
roleChooser.getChildren().add(checkBox);
}
roleManager.setActiveRoles(
activeRoles
);
return roleChooser;
}
private Pane createContent() {
HBox content = new HBox(10);
// icon license:
//License: Free for non-commercial use.
//Commercial usage: Not allowed
//The products or characters depicted in these icons are © by Lucasfilm Ltd.
content.getChildren().addAll(
createLabel("Darth Vader", "Vader-03-icon.png", Role.father),
createLabel("Queen Amidala", "Padme-Amidala-icon.png", Role.mother),
createLabel("Luke Skywalker", "Luke-Skywalker-01-icon.png", Role.brother, Role.son),
createLabel("Princess Leia", "Leia-icon.png", Role.daughter, Role.sister)
);
return content;
}
private Label createLabel(String text, String graphic, Role... roles) {
Label label = new Label(
text,
new ImageView(
new Image(
"http://icons.iconarchive.com/icons/jonathan-rey/star-wars-characters/128/" + graphic
)
)
);
label.setContentDisplay(ContentDisplay.TOP);
roleManager.assignRole(label, roles);
return label;
}
public static void main(String[] args) {
launch(args);
}
}
基于 FXML 的解决方案
我对如何让这样的东西与 FXML 一起工作很感兴趣,所以我创建了一个小框架来处理基于角色的 FXML UI。它可能有一些性能增强,并为定义基于角色的控制器添加一些便利定义,以及 FXML 中角色定义的简写,但它似乎在原则上有效并展示了一种基本方法。