JavaFX Использование FilteredList для дочерних элементов FlowPane - PullRequest
0 голосов
/ 16 июня 2020

У меня есть FlowPane, он содержит различные элементы. Я хотел бы иметь возможность использовать FilteredList<MyElement>, чтобы у пользователя была возможность поиска, но FlowPane имеет только метод getChildren(). Как мне отфильтровать отображаемые узлы в FlowPane?

Ответы [ 2 ]

2 голосов
/ 16 июня 2020

Предполагая, что MyElement является подклассом Node, вы можете просто выполнить

FlowPane flowPane = ... ;
FilteredList<MyElement> someFilteredList = ... ;
Bindings.bindContent(flowPane.getChildren(), someFilteredList)

Вот очень быстрый и грязный пример, который создает 100 меток с целыми значениями в качестве текста. Текстовое поле обновляет фильтр в отфильтрованном списке, поэтому будут отображаться только метки, значения которых кратны введенному значению.

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;


/**
 * JavaFX App
 */
public class App extends Application {

    @Override
    public void start(Stage stage) {

        FlowPane flow = new FlowPane();

        ObservableList<Label> labels = FXCollections.observableArrayList();
        for (int i = 1 ; i <= 100 ; i++) {
            Label label = new Label(Integer.toString(i));
            label.setPadding(new Insets(1,5,0,0));
            labels.add(label);
        }

        FilteredList<Label> filtered = new FilteredList<>(labels);

        TextField filter = new TextField();
        filter.setTextFormatter(new TextFormatter<String>(change -> 
            change.getControlNewText().matches("[0-9]*") ? change : null
        ));

        filter.textProperty().addListener((obs, oldFilter, newFilter) -> {
            String f = newFilter.trim();
            if (f.isEmpty()) {
                filtered.setPredicate(l -> true);
            } else {
                Integer divisor = Integer.parseInt(f);
                filtered.setPredicate(label -> Integer.parseInt(label.getText()) % divisor == 0);
            }
        });

        Bindings.bindContent(flow.getChildren(), filtered);

        BorderPane root = new BorderPane(flow);
        root.setTop(filter);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

}
1 голос
/ 16 июня 2020

Вы можете управлять только элементами списка FlowPane дочерних элементов. Невозможно установить любую реализацию списка, который вы хотите обрабатывать дочерними элементами управления.

Как упоминал @James_D, вы можете использовать Bindings.bindContent. Обновлять предикат только при изменении фильтра (или вы можете получить его bind ed).

public class Controller {

    @FXML
    FlowPane flowPane;
    @FXML
    ComboBox<Class> filterCbx;

    FilteredList<Node> filteredList;

    @FXML
    void initialize() {
        // Set of all the controls classes in flow pane children classes
        ObservableList<Node> flowChildren = flowPane.getChildren();

        filterCbx.getItems().addAll(flowChildren
                .stream()
                .map(Node::getClass)
                .collect(Collectors.toSet()));
        // Node will be treated as show all children functionality
        filterCbx.getItems().add(Node.class);
        filterCbx.setOnAction(this::onFilterChanged);

        // Source need to be an another FXCollection, cant use directly flowChildren
        filteredList = new FilteredList<Node>(FXCollections.observableArrayList(flowChildren));
        Bindings.bindContent(flowChildren, filteredList);
    }

    private void onFilterChanged(ActionEvent actionEvent) {
        ObservableList<Node> flowChildren = flowPane.getChildren();
        Class selectedItem = filterCbx.getSelectionModel().getSelectedItem();
        // Update predicate on selection change
        filteredList.setPredicate(n -> Node.class.equals(selectedItem) || n.getClass().equals(selectedItem));
    }
}

И представление:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
      prefHeight="400.0"
      prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="so.q62406187.Controller">
    <ComboBox fx:id="filterCbx"/>
    <FlowPane fx:id="flowPane">
        <Pane prefHeight="200.0" prefWidth="200.0"/>
        <Button mnemonicParsing="false" text="Button"/>
        <Label text="Label"/>
        <ComboBox prefWidth="150.0"/>
        <HBox prefHeight="100.0" prefWidth="200.0"/>
        <CheckBox mnemonicParsing="false" text="CheckBox"/>
        <VBox prefHeight="200.0" prefWidth="100.0"/>
        <CheckBox mnemonicParsing="false" text="CheckBox"/>
        <Label text="Label"/>
    </FlowPane>
</VBox>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...