JavaFX:将 ObservableList 绑定到哪个视图?

时间:2021-01-03 21:49:05

标签: java javafx binding

我有一个 ObservableList,其中包含一些 Pojo(包含名称和 imageURL 信息)。 该列表在弹出窗口中可见,如附加屏幕所示。

由于 ObservableList 可以更改(过滤等),我也想更新 Popup 中显示的信息。我知道我可以将 ObservableList 绑定到 TableView 或 ListView - 但是我如何保持与附加屏幕中显示的布局(网格)相同的布局?

因此,我正在寻求有关如何实施的建议 - 感谢任何建议。

enter image description here

提前致谢!

2 个答案:

答案 0 :(得分:3)

TableViewListView 不适合这种布局风格。最简单的解决方案是使用 ListChangeListener 并在收到通知时重建弹出内容。看起来您有类似于填充了简单标签节点的 FlowPane 的东西。通过跟踪对 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();
    }

}

答案 1 :(得分:2)

我正准备说出@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)));
    }
}
相关问题