Использование нескольких ящиков выбора для фильтрации списка в JavaFX - PullRequest
2 голосов
/ 22 мая 2019

Я пытаюсь создать функцию фильтра для моего списка, используя несколько ящиков выбора, и я не знаю, как это сделать, поскольку я довольно новичок в JavaFX.

Я провел некоторое исследование и слышу, используяFilterList требуется, но большинство примеров в сети вращаются только с использованием текстового поля.

Так что это мой класс контроллера

@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);
        }

    }

Это мой основной метод, где я запускаю весь пользовательский интерфейс

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 ]

3 голосов
/ 22 мая 2019

Использование FilteredList - правильный подход, если вы хотите отфильтровать элементы в памяти. Если вы посмотрите на документацию , вы увидите, что FilteredList имеет свойство predicate. Это свойство, что неудивительно, составляет Predicate. Интерфейс Predicate является функциональным интерфейсом (что означает, что он может быть целью лямбда-выражения или ссылки на метод), чей абстрактный метод принимает универсальный аргумент типа T и возвращает true или false на основе произвольной логики. Когда вы устанавливаете свойство predicate для FilteredList, оно будет использовать Predicate, чтобы определить, должны ли элементы в источнике ObservableList быть видимыми через FilteredList представление . Когда элементы добавляются и удаляются из источника ObservableList, FilteredList автоматически обновляется.

К сожалению, если вы обновите какое-либо состояние, на котором основан Predicate (например, какой выбор выбран в ChoiceBox es), он не будет автоматически повторно применять Predicate ко всем элементам источника ObservableList. Другими словами, FilteredList не будет автоматически обновляться только потому, что изменилось внутреннее состояние Predicate. Это означает, что каждый раз, когда вы обновляете состояние фильтра, вам нужно создать new Predicate и установить свойство predicate для FilteredList. Вот где создание и использование привязки будет полезно. ( Примечание. Хотя Callable является частью пакета java.util.concurrent, здесь не происходит параллелизма. )

Для создания привязки мы будем использовать Bindings.createObjectBinding(Callable,Observable...). Этот метод принимает Callable (другой функциональный интерфейс ) и массив Observable объектов. Массив Observable s известен как зависимости созданного ObjectBinding и, когда любой из них признан недействительным, ObjectBinding пересчитывает свое значение на основе заданного Callable. Другими словами, Callable вызывается каждый раз, когда один из Observable становится недействительным.

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);

Если вы заметили, это зависимости value свойств каждого из ваших ChoiceBox es. Свойства (то есть экземпляры ReadOnlyProperty и Property) являются реализациями Observable и становятся недействительными, если их значение возможно изменилось. Это означает, что всякий раз, когда пользователь меняет свой выбор, свойство value становится недействительным, а Predicate из FilteredList изменяется. Когда Predicate изменяется, список «повторно фильтруется».

1 голос
/ 22 мая 2019

Итак, подумав, я могу использовать простую версию для демонстрации, и она должна вписаться в вашу более сложную версию. Ключ создает слушателя для каждого 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);
    }

}
...