【发布时间】:2018-10-04 14:01:07
【问题描述】:
这是我之前发布的问题 here 的后续。
在下面的 MCVE 中,我有一个 TableView 显示 Person 对象的列表。在列表上方,我有一个TextField,用于过滤TableView 中列出的项目。
Person 类包含 4 个字段,但我的搜索字段只检查其中 3 个字段中的匹配项:userId、lastName 和 emailAddress。
过滤功能按预期工作。
但是,我现在需要根据匹配的字段和用户Type对结果进行排名。
MCVE 代码
Person.java:
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public final class Person {
private StringProperty userType = new SimpleStringProperty();
private IntegerProperty userId = new SimpleIntegerProperty();
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private StringProperty emailAddress = new SimpleStringProperty();
public Person(String type, int id, String firstName, String lastName, String emailAddress) {
this.userType.set(type);
this.userId.set(id);
this.firstName.set(firstName);
this.lastName.set(lastName);
this.emailAddress.set(emailAddress);
}
public String getUserType() {
return userType.get();
}
public void setUserType(String userType) {
this.userType.set(userType);
}
public StringProperty userTypeProperty() {
return userType;
}
public int getUserId() {
return userId.get();
}
public void setUserId(int userId) {
this.userId.set(userId);
}
public IntegerProperty userIdProperty() {
return userId;
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getEmailAddress() {
return emailAddress.get();
}
public void setEmailAddress(String emailAddress) {
this.emailAddress.set(emailAddress);
}
public StringProperty emailAddressProperty() {
return emailAddress;
}
}
Main.java:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Comparator;
public class Main extends Application {
TableView<Person> tableView;
private TextField txtSearch;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Create the TableView of data
tableView = new TableView<>();
TableColumn<Person, Integer> colId = new TableColumn<>("ID");
TableColumn<Person, String> colFirstName = new TableColumn<>("First Name");
TableColumn<Person, String> colLastName = new TableColumn<>("Last Name");
TableColumn<Person, String> colEmailAddress = new TableColumn<>("Email Address");
// Set the ValueFactories
colId.setCellValueFactory(new PropertyValueFactory<>("userId"));
colFirstName.setCellValueFactory(new PropertyValueFactory<>("firstName"));
colLastName.setCellValueFactory(new PropertyValueFactory<>("lastName"));
colEmailAddress.setCellValueFactory(new PropertyValueFactory<>("emailAddress"));
// Add columns to the TableView
tableView.getColumns().addAll(colId, colFirstName, colLastName, colEmailAddress);
// Create the filter/search TextField
txtSearch = new TextField();
txtSearch.setPromptText("Search ...");
addSearchFilter(getPersons());
// Add the controls to the layout
root.getChildren().addAll(txtSearch, tableView);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
private void addSearchFilter(ObservableList<Person> list) {
FilteredList<Person> filteredList = new FilteredList<Person>(list);
txtSearch.textProperty().addListener(((observable, oldValue, newValue) ->
filteredList.setPredicate(person -> {
// Clear any currently-selected item from the TableView
tableView.getSelectionModel().clearSelection();
// If search field is empty, show everything
if (newValue == null || newValue.trim().isEmpty()) {
return true;
}
// Grab the trimmed search string
String query = newValue.trim().toLowerCase();
// Convert the query to an array of individual search terms
String[] keywords = query.split("[\\s]+");
// Create a single string containing all the data we will match against
// BONUS QUESTION: Is there a better way to do this?
String matchString =
String.valueOf(person.getUserId())
+ person.getLastName().toLowerCase()
+ person.getEmailAddress().toLowerCase();
// Check if ALL the keywords exist in the matchString; if any are absent, return false;
for (String keyword : keywords) {
if (!matchString.contains(keyword)) return false;
}
// All entered keywords exist in this Person's searchable fields
return true;
})));
SortedList<Person> sortedList = new SortedList<>(filteredList);
// Create the Comparator to allow ranking of search results
Comparator<Person> comparator = new Comparator<Person>() {
@Override
public int compare(Person person, Person t1) {
return 0;
}
};
// Set the comparator and bind list to the TableView
sortedList.setComparator(comparator);
tableView.setItems(sortedList);
}
private ObservableList<Person> getPersons() {
ObservableList<Person> personList = FXCollections.observableArrayList();
personList.add(new Person("DECEASED", 123, "Chrissie", "Watkins", "fishfood@email.com"));
personList.add(new Person("VET", 342, "Matt", "Hooper", "m.hooper@noaa.gov"));
personList.add(new Person("VET", 526, "Martin", "Brody", "chiefofpolice@amity.gov"));
personList.add(new Person("NEW", 817, "Larry", "Vaughn", "lvaughn@amity.gov"));
return personList;
}
}
您会看到我的Main 班级中有一个空的Comparator。这是我需要帮助的。我过去创建了能够基于一个字段进行排序的比较器(来自我的previous question):
Comparator<DataItem> byName = new Comparator<DataItem>() {
@Override
public int compare(DataItem o1, DataItem o2) {
String searchKey = txtSearch.getText().toLowerCase();
int item1Score = findScore(o1.getName().toLowerCase(), searchKey);
int item2Score = findScore(o2.getName().toLowerCase(), searchKey);
if (item1Score > item2Score) {
return -1;
}
if (item2Score > item1Score) {
return 1;
}
return 0;
}
private int findScore(String item1Name, String searchKey) {
int sum = 0;
if (item1Name.startsWith(searchKey)) {
sum += 2;
}
if (item1Name.contains(searchKey)) {
sum += 1;
}
return sum;
}
};
不过,我不确定如何使其适用于多个领域。具体来说,我希望能够选择哪些字段应该排名“更高”。
对于这个例子,我想要完成的是按这个顺序对列表进行排序:
-
userId以keyword开头 -
lastName以keyword开头 -
emailAddress以keyword开头 -
lastName包含一个keyword -
emailAddress包含一个keyword - 在匹配范围内,任何
userType = "VET"都应首先列出
我不是在寻找 Google 级别的算法,而只是在寻找某种方式来确定匹配的优先级。我对 Comparator 类不是很熟悉,并且很难理解它的 JavaDocs,因为它适用于我的需求。
StackOverflow 上有几篇关于按多个字段排序的帖子,但我发现的所有帖子都将Person 与Person 进行比较。在这里,我需要将Person 字段与txtSearch.getText() 值进行比较。
我将如何重构 Comparator 以设置这种性质的自定义排序?
【问题讨论】:
标签: java comparator