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();
}
}
2条答案
按热度按时间31moq8wy1#
TableView
以及ListView
不适合那种布局风格。最简单的解决方法是使用ListChangeListener
并在收到通知时重新生成弹出内容。看起来您有一些类似于flowpane的东西,其中充满了简单的标签节点。通过跟踪对pojo列表的更改来维护子节点列表应该相对简单。有一个很好的可重用类,类似于表/列表“视图”类所使用的单元工厂。编辑:我想出了这样的办法。
上述简单测试程序:
vatpfxk52#
我正准备说出@swpalmer所说的:)并准备演示。因为他的回答包含了所有的措辞。。请找到下面的快速演示是什么意思。可能不是一个精确的解决方案,但会给你一个概念性的想法。