使用多个选择框来过滤JavaFX中的列表视图

时间:2019-05-22 02:15:36

标签: java listview javafx fxml scenebuilder

我正在尝试使用多个选择框为我的列表视图创建过滤器功能,由于我对JavaFX还是很陌生,所以我不知道该怎么做。

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

这是我的控制器类

@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类

 @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的主要方法

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

<?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

<?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>

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

2 个答案:

答案 0 :(得分:3)

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

不幸的是,如果您更新ObservableList所基于的任何状态(例如,在FilteredList中选择了哪些选项),它将不会自动将Predicate应用于所有来源ChoiceBox。换句话说,Predicate不会仅因为ObservableList的内部状态已更改而自动更新。这意味着每次更新过滤器状态时,都需要创建一个 new FilteredList并设置Predicate的{​​{1}}属性。这是创建和使用绑定的地方。 (注意:尽管Predicatepredicate软件包的一部分,但这里没有并发发生。

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

Callable

如果您注意到,则依赖性是每个Callable的{​​{3}}属性。属性(即ObservableFilteredList<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的实现,当其值可能更改时无效。这意味着无论用户何时更改选择,ReadOnlyProperty属性都会失效,并且Property中的Observable也将被更改。 value更改后,列表将被“重新过滤”。

答案 1 :(得分:1)

因此,考虑一下之后,我可以使用简单的版本进行演示,它应该适合您更复杂的版本。关键是为每个ChoiceBox创建一个侦听器。当ChoiceBox选择更改时,更新FilteredList谓词。

  

您需要的代码

cbBranch.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) ->{
     System.out.println("Branch: " + newValue);
    filteredData.setPredicate((t) -> {

        switch(cbGenre.getValue())
        {
            case "All":
                switch(newValue)
                {
                    case "All":
                        return true;
                    default:
                        return newValue.equals(t.getBranch());                                
                }

            default:
                return newValue.equals(t.getBranch()) && cbGenre.getValue().equals(t.getGenre());
        }
    });
});

cbGenre.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue)->{
    System.out.println("Genre: " + newValue);
    filteredData.setPredicate((t) -> {
        switch(cbBranch.getValue())
        {
            case "All":
                switch(newValue)
                {
                    case "All":
                        return true;
                    default:
                        return newValue.equals(t.getGenre());
                }
            default:
                return newValue.equals(t.getGenre()) && cbGenre.getValue().equals(t.getBranch());
        }
    });
});
  

完整示例

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
 *
 * @author Sedrick
 */
public class JavaFXApplication38 extends Application {

    @Override
    public void start(Stage primaryStage) {
        ChoiceBox<String> cbGenre = new ChoiceBox();
        cbGenre.getItems().addAll("All", "Horror", "Action");
        cbGenre.setValue("All");
        ChoiceBox<String> cbBranch = new ChoiceBox();
        cbBranch.getItems().addAll("All", "Branch1", "Branch2");
        cbBranch.setValue("All");

        ObservableList<Movie> movieList = FXCollections.observableArrayList();
        movieList.add(new Movie("Horror", "IT", "Branch1", "Released", "Warner Bros"));
        movieList.add(new Movie("Action","John Wick 3" ,"Branch2", "Coming Soon", "Summit Entertainment"));
        FilteredList<Movie> filteredData = new FilteredList<>(movieList, s -> true);

        ListView<Movie> listView = new ListView<>(filteredData);
        listView.setCellFactory((ListView<Movie> param) -> {
            ListCell<Movie> cell = new ListCell<Movie>() {                
                @Override
                protected void updateItem(Movie item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item != null) {
                        setText(item.getTitle());
                    } else {
                        setText("");
                    }
                }
            };

            return cell;
        });

        cbBranch.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) ->{
             System.out.println("Branch: " + newValue);
            filteredData.setPredicate((t) -> {

                switch(cbGenre.getValue())
                {
                    case "All":
                        switch(newValue)
                        {
                            case "All":
                                return true;
                            default:
                                return newValue.equals(t.getBranch());                                
                        }

                    default:
                        return newValue.equals(t.getBranch()) && cbGenre.getValue().equals(t.getGenre());
                }
            });
        });
        cbGenre.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue)->{
            System.out.println("Genre: " + newValue);
            filteredData.setPredicate((t) -> {
                switch(cbBranch.getValue())
                {
                    case "All":
                        switch(newValue)
                        {
                            case "All":
                                return true;
                            default:
                                return newValue.equals(t.getGenre());
                        }
                    default:
                        return newValue.equals(t.getGenre()) && cbGenre.getValue().equals(t.getBranch());
                }
            });
        });

        HBox root = new HBox(new VBox(cbBranch, cbGenre), listView);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

更新!现在,代码有了更好的Predicate,可以处理更多ChoiceBoxes

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
 *
 * @author Sedrick
 */
public class JavaFXApplication38 extends Application {

    @Override
    public void start(Stage primaryStage) {
        ChoiceBox<String> cbGenre = new ChoiceBox();
        cbGenre.getItems().addAll("All", "Horror", "Action");
        cbGenre.setValue("All");
        ChoiceBox<String> cbBranch = new ChoiceBox();
        cbBranch.getItems().addAll("All", "Branch1", "Branch2");
        cbBranch.setValue("All");
        ChoiceBox<String> cbRelease = new ChoiceBox();
        cbRelease.getItems().addAll("All", "Released", "Coming Soon");
        cbRelease.setValue("All");
        ChoiceBox<String> cbParentCompany = new ChoiceBox();
        cbParentCompany.getItems().addAll("All", "Warner Bros", "Summit Entertainment");
        cbParentCompany.setValue("All");
        ChoiceBox<String> cbTitle = new ChoiceBox();
        cbTitle.getItems().addAll("All", "IT", "John Wick 3");
        cbTitle.setValue("All");


        ObservableList<Movie> movieList = FXCollections.observableArrayList();
        movieList.add(new Movie("Horror", "IT", "Branch1", "Released", "Warner Bros"));
        movieList.add(new Movie("Action","John Wick 3" ,"Branch2", "Coming Soon", "Summit Entertainment"));
        FilteredList<Movie> filteredData = new FilteredList<>(movieList, s -> true);

        ListView<Movie> listView = new ListView<>(filteredData);
        listView.setCellFactory((ListView<Movie> param) -> {
            ListCell<Movie> cell = new ListCell<Movie>() {                
                @Override
                protected void updateItem(Movie item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item != null) {
                        setText(item.getTitle());
                    } else {
                        setText("");
                    }
                }
            };

            return cell;
        });
        cbRelease.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) ->{
             System.out.println("Released: " + newValue);
            filteredData.setPredicate((t) -> {               
                return (cbBranch.getValue().equals("All") ? true : t.getBranch().equals(cbBranch.getValue())) && 
                       (cbGenre.getValue().equals("All") ? true : t.getGenre().equals(cbGenre.getValue())) && 
                       (cbParentCompany.getValue().equals("All") ? true : t.getParentCompany().equals(cbParentCompany.getValue())) &&
                       (cbTitle.getValue().equals("All") ? true : t.getTitle().equals(cbTitle.getValue())) && 
                       (cbRelease.getValue().equals("All") ? true : t.getRelease().equals(cbRelease.getValue()));
            });
        });
        cbBranch.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) ->{
             System.out.println("Branch: " + newValue);

            filteredData.setPredicate((t) -> {
                return (cbBranch.getValue().equals("All") ? true : t.getBranch().equals(newValue)) && 
                       (cbGenre.getValue().equals("All") ? true : t.getGenre().equals(cbGenre.getValue())) && 
                       (cbParentCompany.getValue().equals("All") ? true : t.getParentCompany().equals(cbParentCompany.getValue())) &&
                       (cbTitle.getValue().equals("All") ? true : t.getTitle().equals(cbTitle.getValue())) && 
                       (cbRelease.getValue().equals("All") ? true : t.getRelease().equals(cbRelease.getValue()));
            });
        });
        cbGenre.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue)->{
            System.out.println("Genre: " + newValue);
            filteredData.setPredicate((t) -> {
                return (cbBranch.getValue().equals("All") ? true : t.getBranch().equals(cbBranch.getValue())) && 
                       (cbGenre.getValue().equals("All") ? true : t.getGenre().equals(cbGenre.getValue())) && 
                       (cbParentCompany.getValue().equals("All") ? true : t.getParentCompany().equals(cbParentCompany.getValue())) &&
                       (cbTitle.getValue().equals("All") ? true : t.getTitle().equals(cbTitle.getValue())) && 
                       (cbRelease.getValue().equals("All") ? true : t.getRelease().equals(cbRelease.getValue()));
            });
        });

        cbParentCompany.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue)->{
            System.out.println("parent company: " + newValue);
            filteredData.setPredicate((t) -> {
                return (cbBranch.getValue().equals("All") ? true : t.getBranch().equals(cbBranch.getValue())) && 
                       (cbGenre.getValue().equals("All") ? true : t.getGenre().equals(cbGenre.getValue())) && 
                       (cbParentCompany.getValue().equals("All") ? true : t.getParentCompany().equals(cbParentCompany.getValue())) &&
                       (cbTitle.getValue().equals("All") ? true : t.getTitle().equals(cbTitle.getValue())) && 
                       (cbRelease.getValue().equals("All") ? true : t.getRelease().equals(cbRelease.getValue()));
            });
        });

        cbTitle.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue)->{
            System.out.println("title: " + newValue);
            filteredData.setPredicate((t) -> {
                return (cbBranch.getValue().equals("All") ? true : t.getBranch().equals(cbBranch.getValue())) && 
                       (cbGenre.getValue().equals("All") ? true : t.getGenre().equals(cbGenre.getValue())) && 
                       (cbParentCompany.getValue().equals("All") ? true : t.getParentCompany().equals(cbParentCompany.getValue())) &&
                       (cbTitle.getValue().equals("All") ? true : t.getTitle().equals(cbTitle.getValue())) && 
                       (cbRelease.getValue().equals("All") ? true : t.getRelease().equals(cbRelease.getValue()));
            });
        });

        HBox root = new HBox(new VBox(cbBranch, cbGenre, cbRelease, cbParentCompany, cbTitle), listView);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}