问题描述
我正在尝试使用多个选择框为我的列表视图创建过滤器功能,但我不知道该怎么做,因为我是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
的泛型参数并返回true
或false
基于任意逻辑.设置FilteredList
的predicate
属性时,它将使用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
并设置FilteredList
的predicate
属性.这是在其中创建和使用绑定会有所帮助的地方. (注意:虽然Callable
是java.util.concurrent
软件包的一部分,但此处没有并发发生.)
Unfortunately, if you update any state the Predicate
is based on (e.g. what choices are chosen in ChoiceBox
es) 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 Observable
s 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 Observable
s 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
属性.属性(即ReadOnlyProperty
和Property
的实例)是Observable
的实现,当其值可能更改时无效.这意味着每当用户更改选择时,value
属性将无效,并且FilteredList
的Predicate
也会更改. Predicate
更改后,列表将被重新过滤".
If you notice, the dependencies are the value
properties of each of your ChoiceBox
es. 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中的列表视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!