- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个包含ModelItem
项的主/详细面板。每个ModelItem
有一个ListProperty<ModelItemDetail>
,每个ModelItemDetail
有几个StringProperty
。
在详细信息面板中,我想显示一个Label
,其文本将绑定到当前选定的ModelItemDetail
的每个ModelItem
的属性并从中派生。最终值可能取决于其他外部属性,例如在“详细信息”面板上选择CheckBox
(即,如果选中此复选框,则结果中将不包含bProperty
的值)。
此绑定使用Bindings.createStringBinding()
完成了我想要的:
ObservableValue<ModelItem> selectedItemBinding = EasyBind.monadic(mdModel.selectedItemProperty()).orElse(new ModelItem());
// API Label Binding
apiLabel.textProperty().bind(Bindings.createStringBinding(
() -> selectedItemBinding.getValue().getDetails().stream()
.map(i -> derivedBinding(i.aProperty(), i.bProperty()))
.map(v->v.getValue())
.collect(Collectors.joining(", "))
, mdModel.selectedItemProperty(), checkBox.selectedProperty()));
private ObservableValue<String> derivedBinding(ObservableValue<String> aProp, ObservableValue<String> bProp) {
return EasyBind.combine(aProp, bProp, checkBox.selectedProperty(),
(a, b, s) -> Boolean.TRUE.equals(s) ? new String(a + " <" + b + ">") : a);
}
ObservableList
,而必须坚持使用
ObservableValue<ObservableList>>
。通过
EasyBind.map(ObservableList)
和
EasyBind.combine(ObservableList)
将转换链接起来很不方便,这似乎是实现此绑定的理想选择。在某些时候,我已经考虑过创建一个本地ListProperty并将其通过selectedItem上的侦听器绑定到selectedItem的详细信息,但是它看起来太冗长和不干净。
ObservableValue<ObservableList<ModelItemDetail>> ebDetailList = EasyBind.select(selectedItemBinding).selectObject(ModelItem::detailsProperty);
MonadicObservableValue<ObservableList<ObservableValue<String>>> ebDerivedList = EasyBind.monadic(ebDetailList).map(x->EasyBind.map(x, i -> derivedBinding(i.aProperty(), i.bProperty())));
MonadicObservableValue<ObservableValue<String>> ebDerivedValueBinding = ebDerivedList.map(x->EasyBind.combine(x, stream -> stream.collect(Collectors.joining(", "))));
easyBindLabel.textProperty().bind(ebDerivedValueBinding.getOrElse(new ReadOnlyStringWrapper("Nothing to see here, move on")));
getOrElse
仅在初始化时被调用,并且在
selectedItem
更改时不会更新。
ObservableList
,但是没想到其他任何事情都会成为空列表:
ObservableList<ModelItemDetail> ebDetailList = EasyBind.select(selectedItemBinding).selectObject(ModelItem::detailsProperty).get();
ObservableList<ObservableValue<String>> ebDerivedList = EasyBind.map(ebDetailList, i -> derivedBinding(i.aProperty(), i.bProperty()));
ObservableValue<String> ebDerivedValueBinding = EasyBind.combine(ebDerivedList, stream -> stream.collect(Collectors.joining(", "))).orElse("Nothing to see here, move on");
easyBindLabel2.textProperty().bind(ebDerivedValueBinding);
EasyBind.subscribe
来侦听selectedItem的更改并重新绑定(对此不太确定,但是我认为不需要重新绑定,所有东西都可以执行计算):
EasyBind.subscribe(selectedItemBinding, newValue -> {
if (newValue != null) {
ObservableList<ObservableValue<String>> l =
EasyBind.map(newValue.getDetails(),
i -> derivedBinding(i.aProperty(), i.bProperty()));
easyBindLabelSub.textProperty().bind(
EasyBind.combine(l,
strm -> strm.collect(Collectors.joining(", "))
));}});
EasyBind.Subscribe
来订阅checkbox.selectedProperty,它会按预期工作,但这也太冗长和不干净。如果我自己将API侦听器添加到selectedItemProperty并在那里执行绑定,也会发生同样的情况。
package mcve.javafx;
import java.util.*;
import java.util.stream.*;
import javafx.application.*;
import javafx.beans.binding.*;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
import org.fxmisc.easybind.*;
import org.fxmisc.easybind.monadic.*;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
private CheckBox checkShowMore;
@Override
public void start(Stage primaryStage) {
try {
// Initialize model
MasterDetailModel mdModel = new MasterDetailModel();
ObservableList<ModelItem> itemsList = FXCollections.observableArrayList();
for (int i=0;i<5;i++) { itemsList.add(newModelItem(i)); }
// Master
ListView<ModelItem> listView = new ListView<ModelItem>();
listView.setItems(itemsList);
listView.setPrefHeight(150);
mdModel.selectedItemProperty().bind(listView.getSelectionModel().selectedItemProperty());
//Detail
checkShowMore = new CheckBox();
checkShowMore.setText("Show more details");
VBox detailVBox = new VBox();
Label apiLabel = new Label();
Label easyBindLabel = new Label();
Label easyBindLabel2 = new Label();
Label easyBindLabelSub = new Label();
Label easyBindLabelLis = new Label();
detailVBox.getChildren().addAll(
checkShowMore,
new TitledPane("API Binding", apiLabel),
new TitledPane("EasyBind Binding", easyBindLabel),
new TitledPane("EasyBind Binding 2", easyBindLabel2),
new TitledPane("EasyBind Subscribe", easyBindLabelSub),
new TitledPane("Listener+EasyBind Approach", easyBindLabelLis)
);
// Scene
Scene scene = new Scene(new VBox(listView, detailVBox),400,400);
primaryStage.setScene(scene);
primaryStage.setTitle("JavaFX/EasyBind MVCE");
// --------------------------
// -------- BINDINGS --------
// --------------------------
ObservableValue<ModelItem> selectedItemBinding = EasyBind.monadic(mdModel.selectedItemProperty()).orElse(new ModelItem());
// API Label Binding
apiLabel.textProperty().bind(Bindings.createStringBinding(
() -> selectedItemBinding.getValue().getDetails().stream()
.map(i -> derivedBinding(i.aProperty(), i.bProperty()))
.map(v->v.getValue())
.collect(Collectors.joining(", "))
, mdModel.selectedItemProperty(), checkShowMore.selectedProperty()));
// EasyBind Binding Approach 1
{
ObservableValue<ObservableList<ModelItemDetail>> ebDetailList = EasyBind.select(selectedItemBinding).selectObject(ModelItem::detailsProperty);
MonadicObservableValue<ObservableList<ObservableValue<String>>> ebDerivedList = EasyBind.monadic(ebDetailList).map(x->EasyBind.map(x, i -> derivedBinding(i.aProperty(), i.bProperty())));
MonadicObservableValue<ObservableValue<String>> ebDerivedValueBinding = ebDerivedList.map(x->EasyBind.combine(x, stream -> stream.collect(Collectors.joining(", "))));
easyBindLabel.textProperty().bind(ebDerivedValueBinding.getOrElse(new ReadOnlyStringWrapper("Nothing to see here, move on")));
}
// EasyBind Binding Approach 2
{
ObservableList<ModelItemDetail> ebDetailList = EasyBind.select(selectedItemBinding).selectObject(ModelItem::detailsProperty).get();
ObservableList<ObservableValue<String>> ebDerivedList = EasyBind.map(ebDetailList, i -> derivedBinding(i.aProperty(), i.bProperty()));
ObservableValue<String> ebDerivedValueBinding = EasyBind.combine(ebDerivedList, stream -> stream.collect(Collectors.joining(", "))).orElse("Nothing to see here, move on");
easyBindLabel2.textProperty().bind(ebDerivedValueBinding);
}
// Subscribe approach
EasyBind.subscribe(selectedItemBinding, newValue -> {
if (newValue != null) {
ObservableList<ObservableValue<String>> l = EasyBind.map(newValue.getDetails(), i -> derivedBinding(i.aProperty(), i.bProperty()));
easyBindLabelSub.textProperty().bind(
EasyBind.combine(l,
strm -> strm.collect(Collectors.joining(", "))
));
}
});
//With this it works as intended, but something feels very wrong about this
/*
EasyBind.subscribe(checkShowMore.selectedProperty(), newValue -> {
if (selectedItemBinding != null) {
ObservableList<ObservableValue<String>> l = EasyBind.map(selectedItemBinding.getValue().getDetails(), i -> derivedBinding(i.aProperty(), i.bProperty()));
easyBindLabelSub.textProperty().bind(
EasyBind.combine(l,
strm -> strm.collect(Collectors.joining(", "))
));
}
});
*/
// Listener approach
selectedItemBinding.addListener( (ob, o, n) -> {
ObservableList<ModelItemDetail> ebDetailList = n.getDetails();
ObservableList<ObservableValue<String>> ebDerivedList = EasyBind.map(ebDetailList, i -> derivedBinding(i.aProperty(), i.bProperty()));
ObservableValue<String> ebDerivedValueBinding = EasyBind.combine(ebDerivedList, stream -> stream.collect(Collectors.joining(", "))).orElse("Nothing to see here, move on");
easyBindLabelLis.textProperty().bind(ebDerivedValueBinding);
});
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
private ObservableValue<String> derivedBinding(ObservableValue<String> aProp, ObservableValue<String> bProp) {
return EasyBind.combine(aProp, bProp, checkShowMore.selectedProperty(),
(a, b, s) -> Boolean.TRUE.equals(s) ? new String(a + " <" + b + ">") : a);
}
private ModelItem newModelItem(int number) {
ModelItem item = new ModelItem();
item.itemNumber = number+1;
for (int i=0;i<2;i++) {
ModelItemDetail detail = new ModelItemDetail();
detail.setA("A" + (i+item.itemNumber));
detail.setB("B" + (i+item.itemNumber));
item.getDetails().add(detail);
}
return item;
}
/** GUI Model class */
private static class MasterDetailModel {
private ObjectProperty<ModelItem> selectedItemProperty = new SimpleObjectProperty<>();
public ObjectProperty<ModelItem> selectedItemProperty() { return selectedItemProperty; }
public ModelItem getSelectedItem() { return selectedItemProperty.getValue(); }
public void setSelectedItem(ModelItem item) { selectedItemProperty.setValue(item); }
}
/** Domain Model class */
private static class ModelItem {
int itemNumber;
private ListProperty<ModelItemDetail> detailsProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
public ListProperty<ModelItemDetail> detailsProperty() { return detailsProperty; }
public ObservableList<ModelItemDetail> getDetails() { return detailsProperty.getValue(); }
public void setDetails(List<ModelItemDetail> details) { detailsProperty.setValue(FXCollections.observableList(details)); }
public String toString() { return "Item " + itemNumber; }
}
/** Domain Model class */
private static class ModelItemDetail {
private StringProperty aProperty = new SimpleStringProperty();
public StringProperty aProperty() { return aProperty; }
public String getA() { return aProperty.get(); }
public void setA(String a) { aProperty.set(a); }
private StringProperty bProperty = new SimpleStringProperty();
public StringProperty bProperty() { return bProperty; }
public String getB() { return bProperty.get(); }
public void setB(String b) { bProperty.set(b); }
}
}
ListProperty<ModelItemDetail> obsList = new SimpleListProperty<>(FXCollections.observableArrayList(i->new Observable[] { i.aProperty(), i.bProperty(), checkShowMore.selectedProperty()}));
obsList.bind(selectedItemBinding.flatMap(ModelItem::detailsProperty));
ObservableList<ModelItemDetail> ebDetailList = obsList; // WHY ??
ObservableList<ObservableValue<String>> ebDerivedList = EasyBind.map(ebDetailList, i -> derivedBinding(i.aProperty(), i.bProperty()));
ObservableValue<String> ebDerivedValueBinding = EasyBind.combine(ebDerivedList, stream -> stream.collect(Collectors.joining(", "))).orElse("Nothing to see here, move on");
labelPlayground.textProperty().bind(ebDerivedValueBinding);
ObservableList
中获取
selectedItem
。声明一个本地
ListProperty
并将其绑定到所选项目上,我可以利用
ListProperty
作为
ObservableList
的优势。我认为EasyBind不会跟随某个地方。感觉类型信息在某处迷路了。我不能在最后的代码中将所有这些变量放在一起,而且我不明白为什么EasyBind.map()在最后的代码中会接受
ebDetailList
,但不接受
obsList
。
ListProperty
的后备列表中的提取器不执行任何操作。我猜
obsList.bind()
正在用没有提取器的Model中的列表替换后备列表。
最佳答案
如果我理解正确,则希望标签显示所选ModelItem
的文本,该文本由其中包含的所有ModelItemDetail
组成。每当添加或删除ModelItemDetail
以及列表中任何a
的b
或ModelItemDetail
属性更新时,此文本都应更新。
您不需要外部库来进行此1级深度绑定(ModelItemDetail
-> a
,b
)。 ModelItemDetail
列表的更改由ObservableList
报告。列表中各项属性的更改可以通过extractor报告:
ListProperty<ModelItemDetail> detailsProperty = new SimpleListProperty<>(
FXCollections.observableArrayList(i -> new Observable[]{i.aProperty(), i.bProperty()}));
ListProperty
,一个简单的
ObservableList
就足够了。
ModelItem
中显示一个
ListView
。使用具有某些
ModelItemDetail
和
a
属性的3
b
对其进行初始化。
ModelItemDetail
的文本。
CheckBox
确定是否显示
b
属性。请注意,即使未选中它,也会继续报告对
b
的更改(但未显示)。
ModelItemDetail
添加到列表中。此更改将立即通过
ObservableList
反映出来。
a
的
ModelItemDetail
属性的值。此更改将立即通过
ObservableList
的提取器反映出来。
public class Main extends Application {
public Main() {}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
// Mock initial data
ModelItem item = new ModelItem();
ModelItemDetail mid1 = new ModelItemDetail();
mid1.setA("a1");
mid1.setB("b1");
ModelItemDetail mid2 = new ModelItemDetail();
mid2.setA("a2");
mid2.setB("b2");
ModelItemDetail mid3 = new ModelItemDetail();
mid3.setA("a3");
mid3.setB("b3");
ObservableList<ModelItemDetail> details = item.getDetails();
details.add(mid1);
details.add(mid2);
details.add(mid3);
// Create binding
CheckBox showB = new CheckBox("Show b");
Label label = new Label();
label.textProperty().bind(Bindings.createStringBinding(() -> {
return details.stream()
.map(mid ->
Boolean.TRUE.equals(showB.isSelected()) ? new String(mid.getA() + " <" + mid.getB() + ">") : mid.getA()
).collect(Collectors.joining(", "));
}, details, showB.selectedProperty()));
// Create testing components
Button add = new Button("Add item detail");
add.setOnAction(e -> {
Random r = new Random();
int i = r.nextInt(100) + 3;
ModelItemDetail mid = new ModelItemDetail();
mid.setA("a" + i);
mid.setB("b" + i);
details.add(mid);
});
Button changeA = new Button("Change some A");
changeA.setOnAction(e -> {
Random r = new Random();
ModelItemDetail detail = details.get(r.nextInt(details.size()));
detail.setA("a" + r.nextInt(100) + 3);
});
// Display everything
BorderPane pane = new BorderPane();
ListView<ModelItem> list = new ListView<>();
list.getItems().add(item);
pane.setCenter(list);
pane.setRight(new VBox(add, changeA));
pane.setTop(showB);
pane.setBottom(label);
stage.setScene(new Scene(pane));
stage.show();
}
private static class ModelItem {
int itemNumber;
private ObservableList<ModelItemDetail> detailsProperty = FXCollections.observableArrayList(i -> new Observable[]{i.aProperty(), i.bProperty()});
public ObservableList<ModelItemDetail> getDetails() { return detailsProperty; }
@Override public String toString() { return "Item " + itemNumber; }
}
/** Domain Model class */
private static class ModelItemDetail {
private StringProperty aProperty = new SimpleStringProperty();
public StringProperty aProperty() { return aProperty; }
public String getA() { return aProperty.get(); }
public void setA(String a) { aProperty.set(a); }
private StringProperty bProperty = new SimpleStringProperty();
public StringProperty bProperty() { return bProperty; }
public String getB() { return bProperty.get(); }
public void setB(String b) { bProperty.set(b); }
}
}
ModelItem
中添加更多
ListView
,并让标签显示所选文本。
关于java - 使用EasyBind绑定(bind)到ObservableValue <ObservableList>而不是ObservableList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45483472/
我有这个对象: public class Oggetto{ private int value; private boolean valid; public Oggetto(i
我的模型中有一个我想要跟踪的字符串(该字符串代表一个大陆)。如果大陆发生变化,则应在 GUI 中使用该大陆的正确国家/地区更新 ListView。 String selectedContinent;
JavaFX 是一个 UI 框架。尽管如此,我还是想将 JavaFX ObservableValue 用于非 UI(或者至少是这样)。这是一个好主意吗?!我个人不这么认为(UI-Thread,无并发)
如何让所有听众获得一个可观察的值?我可以扩展类并覆盖 addListener和 removeListener方法将它们存储在一个集合中。但是该集合应该已经以某种方式存储在可观察值中。我怎么能拿到那套?
我想创建一个TableView 。在我的.fxml Controller ,我有这两列: @FXML private TableColumn planeRegistrationColumn; @FXM
我不明白 ObservableValue,或者传入 ObservableValue 和传入原始值/类型本身有什么区别? 如果我以下面的例子为例,无论是 ObservableValue 还是 doubl
如何转换 Integer致 ObservableValue在 javafx 2.0 及更高版本中? 最佳答案 我们使用 ReadOnlyObjectWrapper<>(*integer value*)
我将更频繁地使用 RxJava 和 ReactFX,但我想了解的是如何协调两者,因为 ReactFX 没有 RxJava 依赖项,那么两者如何在同一个 monad 中相互通信?对于在没有大量样板的情况
我在 Java 中收到以下错误消息:“无法从 StringProperty 转换为 ObservableValue” 我正在使用 TableView,我想在 TableColumn 中添加数据。我有三
我试图了解 JavaFX 属性 API,并注意到 ReadOnlyBooleanProperty、ReadOnlyStringProperty 和其他 ReadOnlyXPropety 类实现了 Ob
我是一名优秀的程序员,十分优秀!