动态JavaFX绑定

时间:2014-04-07 15:32:12

标签: javafx-2

我的应用程序必须处理动态绑定。 我有一个Java Bean对象列表,其中包含许多必须编辑的属性。 有多种类型的对象具有不同的属性。

我创建一个TreeView来列出对象。 每次我在TreeView中选择一个对象时,我会更新屏幕中的第二个容器,我会动态创建绑定到当前对象属性的Labels和TextField。

我使用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,我清除容器,并将这些新对象添加到:

  • 表示名称的TextField,与Bean接口的JavaBeanStringProperty,并将其与文本字段的TextProperty双向绑定。
  • 表示金额的TextField,与Bean接口的JavaBeanIntegerProperty,并使用NumberConverter将其与文本字段的TextProperty双向绑定。

当我选择Bean2时,我清除容器,然后将这些新对象添加到:

  • 表示名称的TextField,与Bean接口的JavaBeanStringProperty,并将其与文本字段的TextProperty双向绑定。
  • 表示类型的TextField,与Bean接口的JavaBeanStringProperty,并将其与文本字段的TextProperty双向绑定。

当我选择Bean3时,我清除容器,然后将这些新对象添加到:

  • 表示名称的TextField,与Bean接口的JavaBeanStringProperty,并将其与文本字段的TextProperty双向绑定。
  • 表示地址的TextField,与Bean接口的JavaBeanStringProperty,并将其与文本字段的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时,PropertyChangeListener的数量始终在增加。我知道为什么会这样,但我真的不知道该怎么处理。

有更好的方法吗? 注意我无法更改Bean对象。他们不在我的掌控之中。

非常感谢。

1 个答案:

答案 0 :(得分:0)

您是否尝试过使用JFXtras BeanPathAdaptor?它是一个很棒的基于POJOS的动态绑定库。

你只需要使用

beanPathAdaptor.setBean(myNewBean);

当你想改变你的豆子时!

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

修改

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

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

要优化它,您可以将所有已创建的组件保留在内存中并在之后再次使用它们以防止再次支付视图的实例,最好的方法是将它们设置为visible == false当您不使用时如果您愿意,甚至可以在初始化时对所有可能的组件情况进行急切的实例化。