动态JavaFX绑定

3
我的应用程序需要处理动态绑定。 我有一个Java Bean对象列表,其中包含许多必须编辑的属性。 有多种类型的对象具有不同的属性。
我创建了一个TreeView来列出这些对象。 每次在TreeView中选择一个对象时,我会更新屏幕上的第二个容器,在其中动态创建标签和文本字段,这些标签和文本字段与当前对象的属性绑定。
我使用JavaBeanStringProperty、JavaBeanIntegerProperty和其他此类对象来创建与Java Bean交互的属性。这很完美。 我将这些JavaBeanProperty对象中的每一个都链接到其相应的TextField的TextAttribute,以便在UI更改时可以更新Bean,反之亦然。
问题是:每次我在TreeView中选择一个新的Java Bean时,先前动态创建的所有对象似乎仍然存在。它将在我第一次选择Bean并对其进行编辑时工作,但对于进一步的第二次,它将无法工作。
我尝试创建一个已创建绑定的列表,以便在选择新Bean时取消绑定它们,但是这是不可能的,因为StringProperties和IntegerProperties没有共同的接口,所以我无法解除绑定它们。
有人有关于如何处理这个问题的想法吗?
示例:
豆子及其属性:
- Bean1: name (String), amount (integer) - Bean2: name (String), type (String) - Bean3: name (String), address (string)
如果我选择Bean1,我会清除容器,并将这些新对象添加到其中:
- 一个文本字段来表示名称,一个JavaBeanStringProperty与Bean交互,并使用文本字段的TextProperty进行双向绑定。 - 一个文本字段来表示金额,一个JavaBeanIntegerProperty与Bean交互,并使用NumberConverter将其与文本字段的TextProperty进行双向绑定。
当我选择Bean2时,我会清除容器,并将这些新对象添加到其中:
- 一个文本字段来表示名称,一个JavaBeanStringProperty与Bean交互,并使用文本字段的TextProperty进行双向绑定。 - 一个文本字段来表示类型,一个JavaBeanStringProperty与Bean交互,并使用文本字段的TextProperty进行双向绑定。
当我选择Bean3时,我会清除容器,并将这些新对象添加到其中:
- 一个文本字段来表示名称,一个JavaBeanStringProperty与Bean交互,并使用文本字段的TextProperty进行双向绑定。 - 一个文本字段来表示地址,一个JavaBeanStringProperty与Bean交互,并使用文本字段的TextProperty进行双向绑定。
以下是完整的代码示例:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.adapter.JavaBeanIntegerProperty;
import javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;

public class Main extends Application {

    //=============================================================================================
    public abstract class Bean {
        public abstract void createUI(Pane container);
    }

    //=============================================================================================
    public class Bean1 extends Bean {
        private String name;
        private int amount;

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

        public Bean1(String name, int amount) {
            this.name = name;
            this.amount = amount;
        }

        public String toString() { return name; }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            pcs.addPropertyChangeListener(listener);
            System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
        }   

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            pcs.removePropertyChangeListener(listener);
            System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
        }   

        public String getName() { return name; }

        public void setName(String name) {
            String last = this.name;
            this.name = name;
            pcs.firePropertyChange("name", last, this.name);
            System.out.println("Bean 1: name changed: " + last + " -> " + this.name);
        }

        public int getAmount() { return amount; }

        public void setAmount(int amount) {
            int last = this.amount;
            this.amount = amount;
            pcs.firePropertyChange("amount", last, this.amount);
            System.out.println("Bean 1: amount changed: " + last + " -> " + this.amount);
        }

        public void createUI(Pane container) {
            HBox nameContainer = new HBox();
            Label nameLabel = new Label("name: ");
            nameLabel.setPrefWidth(80);
            TextField nameValue = new TextField();
            nameValue.setPrefWidth(140);

            try {
                JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
                nameValue.textProperty().bindBidirectional(wrapper);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 1 name property.");
            }

            nameContainer.getChildren().addAll(nameLabel, nameValue);

            HBox amountContainer = new HBox();
            Label amountLabel = new Label("amount: ");
            amountLabel.setPrefWidth(80);
            TextField amountValue = new TextField();
            amountValue.setPrefWidth(140);

            try {
                JavaBeanIntegerProperty wrapper = new JavaBeanIntegerPropertyBuilder().bean(this).name("amount").build();
                Bindings.bindBidirectional(amountValue.textProperty(), wrapper, new NumberStringConverter());
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 1 amount property.");
            }

            amountContainer.getChildren().addAll(amountLabel, amountValue);

            container.getChildren().clear();
            container.getChildren().addAll(nameContainer, amountContainer);
        }
    }

    //=============================================================================================
    public class Bean2 extends Bean {
        private String name;
        private String type;

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

        public Bean2(String name, String type) {
            this.name = name;
            this.type = type;
        }

        public String toString() { return name; }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            pcs.addPropertyChangeListener(listener);
            System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
        }   

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            pcs.removePropertyChangeListener(listener);
            System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
        }   

        public String getName() { return name; }

        public void setName(String name) {
            String last = this.name;
            this.name = name;
            pcs.firePropertyChange("name", last, this.name);
            System.out.println("Bean 2: name changed: " + last + " -> " + this.name);
        }

        public String getType() { return type; }

        public void setType(String type) {
            String last = this.type;
            this.type = type;
            pcs.firePropertyChange("type", last, this.type);
            System.out.println("Bean 2: type changed: " + last + " -> " + this.type);
        }

        public void createUI(Pane container) {
            HBox nameContainer = new HBox();
            Label nameLabel = new Label("name: ");
            nameLabel.setPrefWidth(80);
            TextField nameValue = new TextField();
            nameValue.setPrefWidth(140);

            try {
                JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
                nameValue.textProperty().bindBidirectional(wrapper);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 2 name property.");
            }

            nameContainer.getChildren().addAll(nameLabel, nameValue);

            HBox typeContainer = new HBox();
            Label typeLabel = new Label("type: ");
            typeLabel.setPrefWidth(80);
            TextField typeValue = new TextField();
            typeValue.setPrefWidth(140);

            try {
                JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("type").build();
                typeValue.textProperty().bindBidirectional(wrapper);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 2 type property.");
            }

            typeContainer.getChildren().addAll(typeLabel, typeValue);

            container.getChildren().clear();
            container.getChildren().addAll(nameContainer, typeContainer);
        }
    }

    //=============================================================================================
    public class Bean3 extends Bean {
        private String name;
        private String address;

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

        public Bean3(String name, String address) {
            this.name = name;
            this.address = address;
        }

        public String toString() { return name; }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            pcs.addPropertyChangeListener(listener);
            System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
        }   

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            pcs.removePropertyChangeListener(listener);
            System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
        }   

        public String getName() { return name; }

        public void setName(String name) {
            String last = this.name;
            this.name = name;
            pcs.firePropertyChange("name", last, this.name);
            System.out.println("Bean 3: name changed: " + last + " -> " + this.name);
        }

        public String getAddress() { return address; }

        public void setAddress(String address) {
            String last = this.address;
            this.address = address;
            pcs.firePropertyChange("type", last, this.address);
            System.out.println("Bean 3: address changed: " + last + " -> " + this.address);
        }

        public void createUI(Pane container) {
            HBox nameContainer = new HBox();
            Label nameLabel = new Label("name: ");
            nameLabel.setPrefWidth(80);
            TextField nameValue = new TextField();
            nameValue.setPrefWidth(140);

            try {
                JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
                nameValue.textProperty().bindBidirectional(wrapper);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 3 name property.");
            }

            nameContainer.getChildren().addAll(nameLabel, nameValue);

            HBox addressContainer = new HBox();
            Label addressLabel = new Label("type: ");
            addressLabel.setPrefWidth(80);
            TextField addressValue = new TextField();
            addressValue.setPrefWidth(140);

            try {
                JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("address").build();
                addressValue.textProperty().bindBidirectional(wrapper);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println("Exception binding Bean 3 address property.");
            }

            addressContainer.getChildren().addAll(addressLabel, addressValue);

            container.getChildren().clear();
            container.getChildren().addAll(nameContainer, addressContainer);
        }
    }

    //=============================================================================================
    private class TreeItemRefresher implements PropertyChangeListener {
        private String property;
        private WeakReference<TreeItem<Bean>> treeItem;

        TreeItemRefresher(String property, TreeItem<Bean> treeItem) {
            this.property = property;
            this.treeItem = new WeakReference<>(treeItem);
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (property.equals(evt.getPropertyName())) {
                // Workaround to repaint the tree item when its value object changes.
                TreeItem<Bean> item = treeItem.get();
                if (item != null) {
                    item.setExpanded(false);
                    item.setExpanded(true);
                }
            }
        }
    }

    //=============================================================================================
    private TreeView<Bean> treeView = new TreeView<>();
    private VBox container = new VBox();

    //=============================================================================================
    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Dynamic Bindings tests.");

        HBox mainContainer = new HBox();

        container.setPadding(new Insets(10));

        // Creating beans.
        Bean1 bean1 = new Bean1("Bean 1", 10);
        Bean2 bean2 = new Bean2("Bean 2", "Type O");
        Bean3 bean3 = new Bean3("Bean 3", "10, Central Park Av.");

        // Creating TreeView
        treeView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Bean>>() {
            @Override
            public void changed(ObservableValue<? extends TreeItem<Bean>> arg0, TreeItem<Bean> oldValue, TreeItem<Bean> newValue) {
                Bean newItem = newValue.getValue();
                newItem.createUI(container);
            }
        });

        TreeItem<Bean> bean1item = new TreeItem<Bean>(bean1);
        bean1.addPropertyChangeListener(new TreeItemRefresher("name", bean1item));

        TreeItem<Bean> bean2item = new TreeItem<Bean>(bean2);            
        bean2.addPropertyChangeListener(new TreeItemRefresher("name", bean2item));

        TreeItem<Bean> bean3item = new TreeItem<Bean>(bean3);
        bean3.addPropertyChangeListener(new TreeItemRefresher("name", bean3item));

        bean1item.setExpanded(true);
        treeView.setRoot(bean1item);
        bean1item.getChildren().addAll(bean2item, bean3item);

        mainContainer.getChildren().addAll(treeView, container);

        Scene scene = new Scene(mainContainer, 500, 300, Color.WHITE);        
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    //=============================================================================================
    public Main() {
    }

    //=============================================================================================
    public static void main(String[] args) {
        launch(Main.class, args);
    }

}

请注意,有时字段的更改无法完美处理。 还要注意随着您更改所选Bean,PropertyChangeListeners的数量始终在增加。我知道为什么会发生这种情况,但我真的不知道该如何处理。

是否有更好的方法呢? 请注意,我无法更改Bean对象。它们不在我控制之下。

非常感谢。

1个回答

0

你尝试过使用JFXtras BeanPathAdaptor吗?它是一个基于POJOS的动态绑定的精彩库。

你只需要使用:

beanPathAdaptor.setBean(myNewBean);

当你想要改变你的bean时!

这里有一个链接: http://ugate.wordpress.com/2012/07/30/javafx-programmatic-pojo-expression-bindings-part-iii/

编辑

我看到的另一种方法是在JavaFX中定义3个自定义组件,每种类型一个。在它们中间,您可以定义与您的bean完全绑定的视图,并添加一个输入方法,例如“setBean1(Bean1 bean)”。

然后,在选择时,实例化此组件,显示它并使用“setBean1(...)”设置其内容。您可以对其他类型执行相同的操作。当然,所有绑定现在都是静态的,但是组件的创建和使用是动态的,它是面向组件的,并且高度耦合(视图模型)。

为了优化它,您可以将所有已创建的组件保留在内存中,并在不使用它们时再次使用它们,最好的方法应该是在不使用它们时将它们设置为visible==false,如果您喜欢,甚至可以在初始化时急切地实例化所有可能的组件情况。


我会看一下。 然而,这里的问题与 UI 元素的动态创建有关。你认为有更好的方法吗? - Marcus
@Marcus 如果你想从Java Bean生成类似表单的组件,也许你应该看看FXForm2:http://dooapp.github.io/FXForm2/ - zenbeni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接