JavaFX:将ObservableList绑定到哪个视图?
我有一个ObservableList,其中包含一些POJO,其中包含名称和imageURL信息。 该列表在弹出窗口中显示,如所附屏幕所示 由于ObservableList可以更改过滤等,我也想更新弹出窗口中显示的信息。我知道我可以将ObservableList绑定到TableView或ListView,但是我如何维护与所附屏幕中所示相同的布局网格呢 因此,我要求就如何实施这一点提供建议——任何建议都将不胜感激 提前谢谢 TableView和ListView不适合这种布局样式。最简单的解决方案是使用ListChangeListener并在收到通知时重新生成弹出内容。看起来您有一些类似于流窗格的内容,其中填充了简单的标签节点。通过跟踪对POJO列表的更改来维护子节点列表应该相对简单。有一个很好的候选可重用类,类似于要生成的表/列表视图类所使用的单元工厂 编辑:我想出了这样的办法JavaFX:将ObservableList绑定到哪个视图?,java,javafx,binding,Java,Javafx,Binding,我有一个ObservableList,其中包含一些POJO,其中包含名称和imageURL信息。 该列表在弹出窗口中显示,如所附屏幕所示 由于ObservableList可以更改过滤等,我也想更新弹出窗口中显示的信息。我知道我可以将ObservableList绑定到TableView或ListView,但是我如何维护与所附屏幕中所示相同的布局网格呢 因此,我要求就如何实施这一点提供建议——任何建议都将不胜感激 提前谢谢 TableView和ListView不适合这种布局样式。最简单的解决方案是使
package stackoverflow.answers.demo;
import java.util.ArrayList;
import java.util.List;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Label;
public class NodeFactory<T> {
private ObservableList<Node> nodes;
private ObservableList<T> modelData;
/** Constructs a factory for the childNodes of a Parent. Tracks the data list
* @param childNodes mutable child node list of a Parent
* @param data the data model
*/
public NodeFactory(ObservableList<Node> childNodes, ObservableList<T> data) {
this.nodes = childNodes;
modelData = data;
data.addListener(this::listListener);
rebuildAllNodes();
}
private List<Node> buildNodesFor(List<? extends T> data) {
ArrayList<Node> newNodes = new ArrayList<>(data.size());
for(T datum : data) {
Node node = buildNodeFor(datum);
newNodes.add(node);
}
return newNodes;
}
private void rebuildAllNodes() {
List<Node> newNodes = buildNodesFor(modelData);
nodes.setAll(newNodes);
}
/**
* Implementations of NodeFactory should override this method to construct the appropriate node for the given model
* data. You need not call the base class implementation which simply constructs a Label based on the String
* representation of the model data, or if the model data is a Node, a Label that wraps the model as its graphic.
* @param model the data to generate a Node for
* @return Node used to render the given model data
*/
protected Node buildNodeFor(T model) {
if (model instanceof Node) {
return new Label(null, (Node) model);
}
return new Label(String.valueOf(model));
}
private void listListener(ListChangeListener.Change<? extends T> c) {
// rebuildAllNodes(); // worst case - we can do better
while (c.next()) {
if (c.wasPermutated()) {
// avoid potentially costly reconstruction of nodes
int from = c.getFrom();
int to = c.getTo();
ArrayList<Node> permutedNodes = new ArrayList<>(to-from);
for (int oldIndex = from; oldIndex < to; oldIndex++) {
int newIndex = c.getPermutation(oldIndex);
permutedNodes.add(newIndex-from, nodes.get(oldIndex));
}
nodes.remove(from, to);
nodes.addAll(from, permutedNodes);
} else if(c.wasUpdated()) {
int to = c.getTo();
for(int index = c.getFrom(); index < to; index++) {
T model = modelData.get(index);
Node node = buildNodeFor(model);
nodes.set(index, node);
}
} else {
if (c.wasRemoved()) {
int index = c.getFrom();
nodes.remove(index, index+c.getRemovedSize());
}
if (c.wasAdded()) {
int from = c.getFrom();
List<Node> newNodes = buildNodesFor(c.getAddedSubList());
nodes.addAll(from, newNodes);
}
}
}
}
}
上述测试的简单测试程序:
public class Main extends Application {
ObservableList<String> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
FlowPane flow = new FlowPane(8,8);
NodeFactory nf = new NodeFactory(flow.getChildren(), data);
flow.setUserData(nf); // just to keep track of it somewhere
Button addButton = new Button("Add Item");
Button removeButton = new Button("Remove Item");
addButton.setOnAction(ae -> data.add("Item "+(data.size()+1)));
removeButton.setOnAction(ae -> data.remove(0));
removeButton.disableProperty().bind(Bindings.isEmpty(flow.getChildren()));
HBox buttons = new HBox(10, addButton, removeButton);
BorderPane bp = new BorderPane(flow, buttons, null, null, null);
Scene scene = new Scene(bp);
stage.setHeight(200);
stage.setScene(scene);
stage.show();
}
}
TableView和ListView不适合这种布局样式。最简单的解决方案是使用ListChangeListener并在收到通知时重新生成弹出内容。看起来您有一些类似于流窗格的内容,其中填充了简单的标签节点。通过跟踪对POJO列表的更改来维护子节点列表应该相对简单。有一个很好的候选可重用类,类似于要生成的表/列表视图类所使用的单元工厂
编辑:我想出了这样的办法
package stackoverflow.answers.demo;
import java.util.ArrayList;
import java.util.List;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Label;
public class NodeFactory<T> {
private ObservableList<Node> nodes;
private ObservableList<T> modelData;
/** Constructs a factory for the childNodes of a Parent. Tracks the data list
* @param childNodes mutable child node list of a Parent
* @param data the data model
*/
public NodeFactory(ObservableList<Node> childNodes, ObservableList<T> data) {
this.nodes = childNodes;
modelData = data;
data.addListener(this::listListener);
rebuildAllNodes();
}
private List<Node> buildNodesFor(List<? extends T> data) {
ArrayList<Node> newNodes = new ArrayList<>(data.size());
for(T datum : data) {
Node node = buildNodeFor(datum);
newNodes.add(node);
}
return newNodes;
}
private void rebuildAllNodes() {
List<Node> newNodes = buildNodesFor(modelData);
nodes.setAll(newNodes);
}
/**
* Implementations of NodeFactory should override this method to construct the appropriate node for the given model
* data. You need not call the base class implementation which simply constructs a Label based on the String
* representation of the model data, or if the model data is a Node, a Label that wraps the model as its graphic.
* @param model the data to generate a Node for
* @return Node used to render the given model data
*/
protected Node buildNodeFor(T model) {
if (model instanceof Node) {
return new Label(null, (Node) model);
}
return new Label(String.valueOf(model));
}
private void listListener(ListChangeListener.Change<? extends T> c) {
// rebuildAllNodes(); // worst case - we can do better
while (c.next()) {
if (c.wasPermutated()) {
// avoid potentially costly reconstruction of nodes
int from = c.getFrom();
int to = c.getTo();
ArrayList<Node> permutedNodes = new ArrayList<>(to-from);
for (int oldIndex = from; oldIndex < to; oldIndex++) {
int newIndex = c.getPermutation(oldIndex);
permutedNodes.add(newIndex-from, nodes.get(oldIndex));
}
nodes.remove(from, to);
nodes.addAll(from, permutedNodes);
} else if(c.wasUpdated()) {
int to = c.getTo();
for(int index = c.getFrom(); index < to; index++) {
T model = modelData.get(index);
Node node = buildNodeFor(model);
nodes.set(index, node);
}
} else {
if (c.wasRemoved()) {
int index = c.getFrom();
nodes.remove(index, index+c.getRemovedSize());
}
if (c.wasAdded()) {
int from = c.getFrom();
List<Node> newNodes = buildNodesFor(c.getAddedSubList());
nodes.addAll(from, newNodes);
}
}
}
}
}
上述测试的简单测试程序:
public class Main extends Application {
ObservableList<String> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
FlowPane flow = new FlowPane(8,8);
NodeFactory nf = new NodeFactory(flow.getChildren(), data);
flow.setUserData(nf); // just to keep track of it somewhere
Button addButton = new Button("Add Item");
Button removeButton = new Button("Remove Item");
addButton.setOnAction(ae -> data.add("Item "+(data.size()+1)));
removeButton.setOnAction(ae -> data.remove(0));
removeButton.disableProperty().bind(Bindings.isEmpty(flow.getChildren()));
HBox buttons = new HBox(10, addButton, removeButton);
BorderPane bp = new BorderPane(flow, buttons, null, null, null);
Scene scene = new Scene(bp);
stage.setHeight(200);
stage.setScene(scene);
stage.show();
}
}
我正准备说出@swpalmer说过的话:并准备演示。因为他的回答有所有的措辞。。请找到下面的快速演示它的意思。可能不是一个精确的解决方案,但会给你一个概念性的想法
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class DynamicContent_In_FlowPane_Demo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<String> srcList = FXCollections.observableArrayList();
srcList.addAll("A1", "A2", "A3", "A4", "A5", "A6", "B1", "B2", "B3", "B4", "B5", "B6", "C1", "C2", "C3", "C4", "C5", "C6");
Map<String, Node> nodeMap = new HashMap<>();
srcList.forEach(item->{
StackPane sp = new StackPane();
sp.setPrefSize(50,50);
sp.setMaxSize(50,50);
sp.getChildren().add(new Label(item));
sp.setStyle("-fx-border-color:black;-fx-border-width:1px;");
nodeMap.put(item,sp);
});
FlowPane fp = new FlowPane();
fp.setMaxSize(202, 252);
fp.setMinSize(202, 252);
fp.setStyle("-fx-border-color:red;-fx-border-width:1px;");
ChoiceBox<String> type = new ChoiceBox<>();
type.getItems().addAll("All", "A", "B", "C","1","2","3","4","5","6");
type.getSelectionModel().select(0);
type.valueProperty().addListener((obs,old,val)->{
if(val.equals("All")){
updateItems(fp,srcList,nodeMap);
}else{
updateItems(fp,srcList.stream().filter(i->i.contains(val)).collect(Collectors.toList()),nodeMap);
}
});
updateItems(fp,srcList,nodeMap);
VBox root = new VBox();
root.getChildren().addAll(type, fp);
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene sc = new Scene(root, 300, 350);
primaryStage.setScene(sc);
primaryStage.setTitle("FlowPane");
primaryStage.show();
}
private void updateItems(FlowPane fp, List<String> list, Map<String, Node> nodeMap) {
fp.getChildren().clear();
list.forEach(item->fp.getChildren().add(nodeMap.get(item)));
}
}
我正准备说出@swpalmer说过的话:并准备演示。因为他的回答有所有的措辞。。请找到下面的快速演示它的意思。可能不是一个精确的解决方案,但会给你一个概念性的想法
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class DynamicContent_In_FlowPane_Demo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<String> srcList = FXCollections.observableArrayList();
srcList.addAll("A1", "A2", "A3", "A4", "A5", "A6", "B1", "B2", "B3", "B4", "B5", "B6", "C1", "C2", "C3", "C4", "C5", "C6");
Map<String, Node> nodeMap = new HashMap<>();
srcList.forEach(item->{
StackPane sp = new StackPane();
sp.setPrefSize(50,50);
sp.setMaxSize(50,50);
sp.getChildren().add(new Label(item));
sp.setStyle("-fx-border-color:black;-fx-border-width:1px;");
nodeMap.put(item,sp);
});
FlowPane fp = new FlowPane();
fp.setMaxSize(202, 252);
fp.setMinSize(202, 252);
fp.setStyle("-fx-border-color:red;-fx-border-width:1px;");
ChoiceBox<String> type = new ChoiceBox<>();
type.getItems().addAll("All", "A", "B", "C","1","2","3","4","5","6");
type.getSelectionModel().select(0);
type.valueProperty().addListener((obs,old,val)->{
if(val.equals("All")){
updateItems(fp,srcList,nodeMap);
}else{
updateItems(fp,srcList.stream().filter(i->i.contains(val)).collect(Collectors.toList()),nodeMap);
}
});
updateItems(fp,srcList,nodeMap);
VBox root = new VBox();
root.getChildren().addAll(type, fp);
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene sc = new Scene(root, 300, 350);
primaryStage.setScene(sc);
primaryStage.setTitle("FlowPane");
primaryStage.show();
}
private void updateItems(FlowPane fp, List<String> list, Map<String, Node> nodeMap) {
fp.getChildren().clear();
list.forEach(item->fp.getChildren().add(nodeMap.get(item)));
}
}
我认为ControlsFX GridView可能在这里工作。我认为ControlsFX GridView可能在这里工作。非常感谢你的贡献。这是有道理的,我希望FX能提供一些开箱即用的东西。但你是对的,这有点直截了当:@Remo我添加了一个简单的工厂实现非常感谢你的贡献。这是有道理的,我希望FX能提供一些开箱即用的东西。但你是对的,这有点直截了当:@Remo我添加了一个简单的工厂实现感谢你的宝贵意见-我将用这种方式尝试:请使用java命名约定..谢谢你的宝贵意见-我将用这种方式尝试:请使用java命名约定。。