本文介绍了使用多个选择框来过滤JavaFX中的列表视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用多个选择框为我的列表视图创建过滤器功能,但我不知道该怎么做,因为我是JavaFX的新手.

I am trying to create a filter function for my listview using multiple choiceboxes and I have no idea how to do it since i'm quite new to JavaFX.

我做了一些研究,听说需要使用filteredList,但是大多数在线示例仅使用文本字段.

I did some research and I hear using a filteredList is required but most of the examples online revolve around only using a textfield.

这是我的控制器类

@FXML
private ChoiceBox<String> genre;
@FXML
private ChoiceBox<String> branch;
@FXML
private ChoiceBox<String> status;
@FXML
private ChoiceBox<String> company;
@FXML
private ListView<Movie> listView;

private ObservableList<Movie> movieList = FXCollections.observableArrayList();
private FilteredList<Movie> filteredData = new FilteredList<>(movieList, s -> true);

public Controller()  {
        vehicleList.addAll(
                new Movie("Horror" ,"IT", ,"Branch1", "Released", "Warner Bros"),
                new Movie("Action","John Wick 3" ,"Branch2", "Coming Soon", "Summit Entertainment")
        );

@Override
public void initialize(URL location, ResourceBundle resources) {
//I am planning to implement the filter here in the initialize method
        listView.setItems(filteredData);
        listView.setCellFactory(movieListView -> new MovieListViewCell());
}

这是MovieListViewCell类

This is the MovieListViewCell class

 @FXML
    private Label genre;

    @FXML
    private Label status;

    @FXML
    private GridPane gridPane;

    private FXMLLoader mLLoader;

    @Override
    protected void updateItem(Movie movie, boolean empty) {
        super.updateItem(vehicle, empty);

        if(empty || vehicle == null) {

            setText(null);
            setGraphic(null);

        } else {
            if (mLLoader == null) {
                mLLoader = new FXMLLoader(getClass().getResource("/application/ListCell.fxml"));
                mLLoader.setController(this);

                try {
                    mLLoader.load();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            genre.setText(String.valueOf(vehicle.getMovie_Genre()));
            status.setText(String.valueOf(vehicle.getMovie_Status()));
            setText(null);
            setGraphic(gridPane);
        }

    }

这是我运行整个UI的主要方法

This my main method where i run the whole UI

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/application/MainPage.fxml"));
            Scene scene = new Scene(root,800,650);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

这是我的FXML主布局MainPage.fxml

This is my FXML main Layout MainPage.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <center>
      <AnchorPane prefHeight="374.0" prefWidth="262.0" BorderPane.alignment="CENTER">
         <children>
            <ListView fx:id="listView" layoutX="106.0" layoutY="93.0" prefHeight="374.4" prefWidth="400.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
         </children>
      </AnchorPane>
   </center>
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu mnemonicParsing="false" text="File">
            <items>
              <MenuItem mnemonicParsing="false" text="Close" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Edit">
            <items>
              <MenuItem mnemonicParsing="false" text="Delete" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Help">
            <items>
              <MenuItem mnemonicParsing="false" text="About" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
   <left>
      <VBox alignment="CENTER" prefHeight="368.0" prefWidth="149.0" BorderPane.alignment="CENTER">
         <children>
            <TextField fx:id="filterField" />
            <ChoiceBox fx:id="type" prefWidth="150.0">
                 <items>
                    <FXCollections fx:factory="observableArrayList">
                        <String fx:value="Horror" />
                        <String fx:value="Action" />
                    </FXCollections>
                 </items>
               <VBox.margin>
                  <Insets bottom="20.0" left="10.0" right="10.0" />
               </VBox.margin>
            </ChoiceBox>
            <ChoiceBox fx:id="branch" prefWidth="150.0">
                <items>
                    <FXCollections fx:factory="observableArrayList">
                        <String fx:value="branch1" />
                        <String fx:value="branch2" />
                        <String fx:value="branch3" />
                    </FXCollections>
                 </items>
               <VBox.margin>
                  <Insets bottom="20.0" left="10.0" right="10.0" />
               </VBox.margin>
            </ChoiceBox>
            <ChoiceBox fx:id="company" prefWidth="150.0">
                <items>
                    <FXCollections fx:factory="observableArrayList">
                        <String fx:value="Warner Bros" />
                        <String fx:value="Summit Entertainment" />
                    </FXCollections>
                 </items>
               <VBox.margin>
                  <Insets bottom="20.0" left="10.0" right="10.0" />
               </VBox.margin>
            </ChoiceBox>
            <ChoiceBox fx:id="status" prefWidth="150.0">
                <items>
                    <FXCollections fx:factory="observableArrayList">
                        <String fx:value="Released" />
                        <String fx:value="Coming Soon" />
                    </FXCollections>
                 </items>
               <VBox.margin>
                  <Insets left="10.0" right="10.0" />
               </VBox.margin>
            </ChoiceBox>
         </children>
      </VBox>
   </left>
</BorderPane>

这是ListCell.fxml

This is ListCell.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>

<GridPane fx:id="gridPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="206.0" prefWidth="534.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
    <columnConstraints>
      <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" maxWidth="350.0" minWidth="0.0" prefWidth="284.0" />
        <ColumnConstraints hgrow="SOMETIMES" maxWidth="509.0" minWidth="0.0" prefWidth="56.0" />
        <ColumnConstraints hgrow="SOMETIMES" maxWidth="543.0" minWidth="10.0" prefWidth="55.0" />
      <ColumnConstraints hgrow="SOMETIMES" maxWidth="543.0" minWidth="10.0" prefWidth="119.0" />
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    </rowConstraints>
    <children>
      <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0">
         <children>
            <ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@../../../../Desktop/Test.jpg" />
               </image>
            </ImageView>
         </children>
      </VBox>
      <VBox alignment="CENTER" prefHeight="212.0" prefWidth="95.0" GridPane.columnIndex="1">
         <children>
            <Label text="Genre:" />
            <Label text="Status:" />
         </children>
      </VBox>
      <VBox alignment="CENTER" prefHeight="183.0" prefWidth="80.0" GridPane.columnIndex="2">
         <children>
            <Label fx:id="genre" text="Label" />
            <Label fx:id="status" text="Label" />
         </children>
      </VBox>
      <VBox alignment="CENTER" prefHeight="225.0" prefWidth="132.0" GridPane.columnIndex="3">
         <children>
            <Button mnemonicParsing="false" text="Button" />
         </children>
      </VBox>
    </children>
</GridPane>

希望有人可以为此提供任何解决方案.谢谢

Hope someone can give me any solution for this. Thanks

推荐答案

如果要过滤内存中的项目,则使用FilteredList是正确的方法.如果您查看文档,则您将会看到FilteredList具有predicate属性.毫无疑问,此属性包含 Predicate . Predicate接口是功能接口(表示它可以是lambda表达式或方法引用的目标),其抽象方法接受类型为T的泛型参数并返回truefalse基于任意逻辑.设置FilteredListpredicate属性时,它将使用Predicate来确定源ObservableList中的元素是否应该通过FilteredList view 可见.在从源ObservableList添加和删除项目时,FilteredList将自动更新.

Using a FilteredList is the correct approach if you want to filter the items in memory. If you look at the documentation, you'll see that FilteredList has a predicate property. This property holds, unsurprisingly, a Predicate. The Predicate interface is a functional interface (which means it can be the target of a lambda expression or method reference) whose abstract method accepts a generic argument of type T and returns true or false based on arbitrary logic. When you set the predicate property of a FilteredList, it will use the Predicate to determine whether or not elements in the source ObservableList should be visible through the FilteredList view. As items are added and removed from the source ObservableList the FilteredList will automatically update.

不幸的是,如果您更新Predicate所基于的任何状态(例如,在ChoiceBox es中选择了哪些选项),它将不会自动将Predicate应用于源ObservableList的所有元素.换句话说,FilteredList不会仅因为Predicate的内部状态已更改而自动更新.这意味着每次更新过滤器状态时,都需要创建一个 new Predicate并设置FilteredListpredicate属性.这是在其中创建和使用绑定会有所帮助的地方. (注意:虽然Callablejava.util.concurrent软件包的一部分,但此处没有并发发生.)

Unfortunately, if you update any state the Predicate is based on (e.g. what choices are chosen in ChoiceBoxes) it will not automatically reapply the Predicate on all the elements of the source ObservableList. In other words, the FilteredList will not automatically update just because the Predicate's internal state has changed. This means every time you update the filter state you need to create a new Predicate and set the predicate property of the FilteredList. This is where creating and using a binding will be helpful. (Note: While Callable is part of the java.util.concurrent package, there is no concurrency happening here.)

要创建绑定,我们将使用 Bindings.createObjectBinding(Callable,Observable...) .该方法接受 Callable (另一个功能接口)和一个Observable对象数组. Observable的数组被称为创建的ObjectBinding dependencies ,当它们中的任何一个无效时,都会导致ObjectBinding根据给定的Callable重新计算其值. .换句话说,每次Observable中的一个失效时,都会调用Callable.

To create the binding, we'll use Bindings.createObjectBinding(Callable,Observable...). That method accepts a Callable (another functional interface) and an array of Observable objects. The array of Observables are known as the dependencies of the created ObjectBinding and, when any of them are invalidated, causes the ObjectBinding to recompute its value based on the given Callable. In other words, the Callable is invoked every time one of the Observables are invalidated.

FilteredList<Movie> filteredList = movieList.filtered(null); // a null Predicate means "always true"

// moved to own variable for clarity (usually inlined with the method call)
Observable[] dependencies = {genre.valueProperty(), branch.valueProperty(), status.valueProperty(), company.valueProperty()};

ObjectBinding<Predicate<Movie>> binding = Bindings.createObjectBinding(() -> {
    Predicate<Movie> predicate = movie -> {
        // test "movie" based on the values of your ChoiceBoxes
    };
    return predicate;
}, dependencies);

filteredList.predicateProperty().bind(binding);

如果您注意到,则依赖项为ChoiceBox es的> value 属性.属性(即ReadOnlyPropertyProperty的实例)是Observable的实现,当其值可能更改时无效.这意味着每当用户更改选择时,value属性将无效,并且FilteredListPredicate也会更改. Predicate更改后,列表将被重新过滤".

If you notice, the dependencies are the value properties of each of your ChoiceBoxes. Properties (i.e. instances of ReadOnlyProperty and Property) are implementations of Observable and are invalidated when their value has possibly changed. This means whenever the user changes their choice, the value property is invalidated, and the Predicate of the FilteredList is changed. When the Predicate changes the list is "re-filtered".

这篇关于使用多个选择框来过滤JavaFX中的列表视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 12:38