有没有办法在 JAVAFX 上实现像“渲染”这样的属性?

Posted

技术标签:

【中文标题】有没有办法在 JAVAFX 上实现像“渲染”这样的属性?【英文标题】:Is there a way to implement a property like "rendered" on JAVAFX? 【发布时间】:2013-11-09 03:00:16 【问题描述】:

如果满足条件,有没有办法只在 Javafx 中“渲染”组件? 我有兴趣做一个具有不同角色的用户界面,只需添加 一个组件,如果角色允许的话,我也想继续使用 FXML。 我没有读过类似的东西。

【问题讨论】:

【参考方案1】:

绑定可见性

Bind 将组件的visible property 转换为BooleanExpression,表示它应该可见的条件。

FXML 可以做binding,但请注意,从 JavaFX 2.2 开始,“当前仅支持解析为属性值或页面变量的简单表达式。将来可能会添加对涉及布尔或其他运算符的更复杂表达式的支持。 "

如果不绑定 FXML,可以使用 JavaFX API 绑定 Java 代码。

node.visibleProperty().bind(conditionProperty);

如果不希望不可见属性占用布局空间,也请先将managed property绑定到可见属性。

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 一起工作很感兴趣,所以我创建了一个 small framework to handle role based FXML UIs。它可能会增强一些性能并添加一些方便的定义来定义基于角色的控制器以及 FXML 中角色定义的简写,但它似乎在原则上有效并展示了一种基本方法。

【讨论】:

@jewelsea 的另一个好答案。绑定节点的可见和可管理属性不是真正的“渲染”,但是,节点仍然存在于场景图中,只是属性发生了变化。真正的“未渲染”将通过 parent.getChildren().remove(node) 删除节点,但在 OP 的要求范围内,这个答案是正确的,它的替代方法是使用 node#userData。 另一种方法是将相关节点扩展为“基于角色的节点”并添加新的属性字段,如roles="ADMIN,CUSTOMER",然后解析、计算并将结果设置为节点的可见属性。 @jewelsea 嗯,这实际上比我期望的答案要多得多,我想这会像前十行一样,所以谢谢,这是一个很大的帮助。我今晚会检查要点,但这里发布的代码足以回答

以上是关于有没有办法在 JAVAFX 上实现像“渲染”这样的属性?的主要内容,如果未能解决你的问题,请参考以下文章

在 CheckListView JavaFX 上实现全选

渲染前Javafx文本的尺寸

在 JavaFX 中禁用按钮

有没有办法强制 JavaFX 释放视频内存?

JavaFX,将 ArrayList 转换为 ObservableList

JavaFX ImageView 没有任何平滑