Это продолжение предыдущего вопроса, который я разместил здесь .
В MCVE ниже у меня есть TableView
, отображающий список Person
объектов.Над списком у меня есть один TextField
, который я использую для фильтрации перечисленных элементов в TableView
.
. Класс Person
содержит 4 поля, но мое поле поиска только проверяет совпаденияв 3 из них: userId
, lastName
и emailAddress
.
Функция фильтрации работает, как и ожидалось.
Однако теперь мне нужно оценитьрезультаты, основанные на том, какие поля были сопоставлены и пользователя Type
.
MCVE CODE
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;
}
}
Вы увидите, что у меня пусто Comparator
вмой Main
класс.Это то, что мне нужно помочь.В прошлом я создал компараторы, которые могут сортировать по одному полю (из моего предыдущего вопроса ):
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
для настройки пользовательской сортировки такого типа?