绑定到一个本身包含在属性中的对象的属性似乎是在典型的应用程序中做了很多事情,在JavaFX中有比在下面做的更好的方法吗?
要解释的更多细节:我想在JavaFX 2.2中制作GUI,用于管理许多项目。我创建了一个小例子来测试所有项目,其中项目是人。这组人员以自定义方式显示(不是列表或树,但我认为这不重要),我可以选择一个。
在侧面板中,我可以编辑当前选定的人。更新会立即显示在一组人员中,当我选择其他人时,编辑面板会更新。
JavaFX的双向绑定似乎非常适合此目的。我目前有这个用于“人物编辑”窗格的fx:controller:
public class PersonEditor implements ChangeListener<Person> {
@FXML private TextField nameField;
@FXML private TextField ageField;
@FXML private TextField heightField;
public void setSelection(ObjectProperty<Person> selectedPersonProperty) {
selectedPersonProperty.addListener(this);
}
@Override
public void changed(ObservableValue<? extends Person> observable, Person oldVal, Person newVal) {
if (oldVal != null) {
nameField.textProperty().unbindBidirectional(oldVal.nameProperty());
ageField.textProperty().unbindBidirectional(oldVal.ageProperty());
heightField.textProperty().unbindBidirectional(oldVal.heightProperty());
}
if (newVal != null) {
nameField.textProperty().bindBidirectional(newVal.nameProperty());
ageField.textProperty().bindBidirectional(newVal.ageProperty());
heightField.textProperty().bindBidirectional(newVal.heightProperty());
}
}
}
我想知道是否有更好的方法,也许JavaFX中的某些内容可以绑定到可以更改的对象的属性?我不喜欢我必须手动取消绑定所有属性的事实,感觉就像重复的代码。 或者这在JavaFx中是否简单?
答案 0 :(得分:3)
就个人而言,我没有发现你在事件处理程序中编写的代码是那些笨拙或笨拙的代码。这是事件处理程序通常在GUI,imo。
中执行的操作问问自己,虽然......在你的环境中有必要具有约束力吗?
如果您必须对在一个面板中进行的编辑的实时更新反映在另一个面板中,那么您可能已经实现了最简单的解决方案。然而,这种UI设计存在固有的困难,并且它可能不是所有情况下的最佳选择。如果用户需要取消他所做的编辑怎么办?如果他改变主意,你有办法回滚编辑吗?有时,编辑的实时更改是不可取的,在这种情况下,将数据模型对象绑定到UI对象可能不是一个好主意。
答案 1 :(得分:2)
似乎这不是可以在JavaFX中更优雅地完成的事情。绑定和解除绑定似乎是最干净的方式。
我自己实现了一种方法。不确定我最终是否会最终使用它(因为它只是用难以阅读的代码替换代码重复)。但它有效,这是我自己的问题的答案,所以我在这里添加了它。
新的PersonEditor类:
public class PersonEditor implements Initializable {
private SelectedObjectPropertyBinder<Person> selectedObjectPropertyBinder =
new SelectedObjectPropertyBinder<Person>();
@FXML private TextField nameField;
@FXML private TextField ageField;
@FXML private TextField heightField;
@Override
public void initialize(URL url, ResourceBundle rb) {
selectedObjectPropertyBinder.getBinders().add(
new ObjectPropertyBindHelper<Person>(nameField.textProperty()) {
@Override public Property objectProperty(Person p)
{ return p.nameProperty(); }
});
selectedObjectPropertyBinder.getBinders().add(
new ObjectPropertyBindHelper<Person>(ageField.textProperty()) {
@Override public Property objectProperty(Person p)
{ return p.ageProperty(); }
});
selectedObjectPropertyBinder.getBinders().add(
new ObjectPropertyBindHelper<Person>(heightField.textProperty()) {
@Override public Property objectProperty(Person p)
{ return p.heightProperty(); }
});
}
public void setSelection(ObjectProperty<Person> selectedPersonProperty) {
selectedObjectPropertyBinder.
setSelectedObjectProperty(selectedPersonProperty);
}
}
辅助类:
public class SelectedObjectPropertyBinder<T> implements ChangeListener<T> {
private List<ObjectPropertyBindHelper<T>> binders =
new ArrayList<ObjectPropertyBindHelper<T>>();
public void setSelectedObjectProperty(Property<T> selectionProperty) {
selectionProperty.addListener(this);
}
public List<ObjectPropertyBindHelper<T>> getBinders() {
return binders;
}
@Override
public void changed(ObservableValue<? extends T> observable,
T oldVal, T newVal) {
if (oldVal != null)
for (ObjectPropertyBindHelper b : binders)
b.unbindBi(oldVal);
if (newVal != null)
for (ObjectPropertyBindHelper b : binders)
b.bindBi(newVal);
}
}
public abstract class ObjectPropertyBindHelper<T> {
private Property boundProperty;
public ObjectPropertyBindHelper(Property boundProperty) {
this.boundProperty = boundProperty;
}
public void bindBi(T o) {
boundProperty.bindBidirectional(objectProperty(o));
}
public void unbindBi(T o) {
boundProperty.unbindBidirectional(objectProperty(o));
}
public abstract Property objectProperty(T t);
public Property getBoundProperty() {
return boundProperty;
}
}
正如斯科特在他的回答中指出的那样,绑定链接并不总是你想要的。如果你想能够取消/提交更改,你也可以使用类似的东西来实现(但是阅读生成的代码可能会更难)。
答案 2 :(得分:0)
这个问题很久以前就被问到了:)无论如何,最近我正在寻找解决方案并以这种方式实现 - 单例 Form 类实现了抽象类:
public abstract class InputForm {
private ArrayList<BindingPair> bindedProps;
private ArrayList<ListenerPair> listenerPairs;
public InputForm() {
bindedProps = new ArrayList();
listenerPairs = new ArrayList();
}
public void resetBindings() {
unbindAll();
bindedProps = new ArrayList();
}
public void resetListeners() {
removeAllListeners();
listenerPairs = new ArrayList();
}
private void unbindAll() {
for (BindingPair pair : bindedProps) {
unbind(pair);
}
}
private void removeAllListeners() {
for (ListenerPair listenerPair : listenerPairs) {
removeListener(listenerPair);
}
}
public void bind(BindingPair bindingPair) {
BindingUtils.bind(bindingPair);
bindedProps.add(bindingPair);
}
public void addListener(ListenerPair listenerPair) {
ListenerUtils.addListener(listenerPair);
listenerPairs.add(listenerPair);
}
private void unbind(BindingPair bindingPair) {
BindingUtils.unbind(bindingPair);
}
private void removeListener(ListenerPair listenerPair) {
ListenerUtils.removeListener(listenerPair);
}
}
public class BindingUtils {
public static void bind(BindingPair bindingPair) {
if (bindingPair.isBidirectionalBinding()) {
if (bindingPair.getStringConverter() != null)
Bindings.bindBidirectional(bindingPair.propertyToBindProperty(), bindingPair.propertyBindToProperty(), bindingPair.getStringConverter());
else
bindingPair.propertyToBindProperty().bindBidirectional(bindingPair.propertyBindToProperty());
} else {
bindingPair.propertyToBindProperty().bind(bindingPair.propertyBindToProperty());
}
}
public static void unbind(BindingPair bindingPair) {
if (bindingPair.isBidirectionalBinding()) {
bindingPair.propertyToBindProperty().unbindBidirectional(bindingPair.propertyBindToProperty());
} else {
bindingPair.propertyToBindProperty().unbind();
}
}
}
public class ListenerUtils {
public static void addListener(ListenerPair listenerPair) {
if (listenerPair.propertyProperty() != null)
listenerPair.propertyProperty().addListener(listenerPair.getListener());
if (listenerPair.roPropertyProperty() != null)
listenerPair.roPropertyProperty().addListener(listenerPair.getListener());
}
public static void removeListener(ListenerPair listenerPair) {
if (listenerPair.propertyProperty() != null)
listenerPair.propertyProperty().removeListener(listenerPair.getListener());
if (listenerPair.roPropertyProperty() != null)
listenerPair.roPropertyProperty().removeListener(listenerPair.getListener());
}
}
public class BindingPair {
private Property propertyToBind;
private Property propertyBindTo;
private boolean bidirectionalBinding;
private StringConverter stringConverter;
public BindingPair(Property propertyToBind, Property propertyBindTo, boolean bidirectionalBinding, StringConverter stringConverter) {
this.propertyToBind = propertyToBind;
this.propertyBindTo = propertyBindTo;
this.bidirectionalBinding = bidirectionalBinding;
this.stringConverter = stringConverter;
}
public Property propertyToBindProperty() {
return propertyToBind;
}
public Property propertyBindToProperty() {
return propertyBindTo;
}
public boolean isBidirectionalBinding() {
return bidirectionalBinding;
}
public StringConverter getStringConverter() {
return stringConverter;
}
}
public class ListenerPair {
private Property property = null;
private ChangeListener listener;
private ReadOnlyObjectProperty roProperty = null;
public ListenerPair(Property property, ChangeListener listener) {
this.property = property;
this.listener = listener;
}
public ListenerPair(ChangeListener listener, ReadOnlyObjectProperty roProperty) {
this.listener = listener;
this.roProperty = roProperty;
}
public Property propertyProperty() {
return property;
}
public ChangeListener getListener() {
return listener;
}
public Object getRoProperty() {
return roProperty.get();
}
public ReadOnlyObjectProperty roPropertyProperty() {
return roProperty;
}
}
在 Form 类(实现 InputForm)中,每次通过此表单“渲染”新对象时都会调用两个方法:
private void doBinding() {
resetBindings();
bind(new BindingPair(dataXlsFileUrlProp, getControllerMain().getSceneRenderer().getLoadedSceneInfo().xlsDataFileProperty(), false, null));
}
private void addListeners() {
resetListeners();
addListener(new ListenerPair(dataXlsFileUrlProp, (observable, oldValue, newValue) -> {
//do smth
}));
}
希望有人能帮忙。