Мое приложение содержит TextField
и ListView
.TextField
позволяет пользователям вводить поисковые термины, которые будут фильтровать содержимое ListView
при вводе.
Процесс фильтрации будет сопоставлять несколько полей в каждом DataItem
в ListView
и возвращатьрезультаты, если какой-либо из них совпадает.
Однако я хочу, чтобы эти результаты располагали по приоритетам элементы, которые соответствуют одному конкретному полю над остальными.
Например, в MCVE ниже,У меня есть два предмета: Computer
и Paper
.Элемент Computer
имеет keyword
для «бумаги», поэтому при поиске «paper» в результате будет возвращено Computer
.
Однако, поскольку у меня также есть элемент с именем Paper
,поиск должен вернуть Paper
вверху списка.В MCVE, тем не менее, результаты по-прежнему расположены в алфавитном порядке:
Вопрос: Как мне обеспечить соответствие любымDataItem.name
перечисленные выше соответствуют DataItem.keywords
?
РЕДАКТИРОВАТЬ: При вводе «pap» в поле поиска также должно возвращаться «Paper» вверху, а затем оставшиесясовпадает, так как частичный поисковый термин частично соответствует имени DataItem
.
MCVE
DataItem.java: import java.util.List;
public class DataItem {
// Instance properties
private final IntegerProperty id = new SimpleIntegerProperty();
private final StringProperty name = new SimpleStringProperty();
private final StringProperty description = new SimpleStringProperty();
// List of search keywords
private final ObjectProperty<List<String>> keywords = new SimpleObjectProperty<>();
public DataItem(int id, String name, String description, List<String> keywords) {
this.id.set(id);
this.name.set(name);
this.description.set(description);
this.keywords.set(keywords);
}
/**
* Creates a space-separated String of all the keywords; used for filtering later
*/
public String getKeywordsString() {
StringBuilder sb = new StringBuilder();
for (String keyword : keywords.get()) {
sb.append(keyword).append(" ");
}
return sb.toString();
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public String getDescription() {
return description.get();
}
public StringProperty descriptionProperty() {
return description;
}
public List<String> getKeywords() {
return keywords.get();
}
public ObjectProperty<List<String>> keywordsProperty() {
return keywords;
}
@Override
public String toString() {
return name.get();
}
}
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.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main extends Application {
// TextField used for filtering the ListView
TextField txtSearch = new TextField();
// ListView to hold our DataItems
ListView<DataItem> dataItemListView = new ListView<>();
// The ObservableList of DataItems
ObservableList<DataItem> dataItems;
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));
// Add the search field and ListView to the layout
root.getChildren().addAll(txtSearch, dataItemListView);
// Build the dataItems List
dataItems = FXCollections.observableArrayList(buildDataItems());
// Add the filter logic
addSearchFilter();
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
/**
* Adds the functionality to filter the list dynamically as search terms are entered
*/
private void addSearchFilter() {
// Wrap the dataItems list in a filtered list, initially showing all items, alphabetically
FilteredList<DataItem> filteredList = new FilteredList<>(
dataItems.sorted(Comparator.comparing(DataItem::getName)));
// Add the predicate to filter the list whenever the search field changes
txtSearch.textProperty().addListener((observable, oldValue, newValue) ->
filteredList.setPredicate(dataItem -> {
// Clear any selection already present
dataItemListView.getSelectionModel().clearSelection();
// If the search field is empty, show all DataItems
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Compare the DataItem's name and keywords with the search query (ignoring case)
String query = newValue.toLowerCase();
if (dataItem.getName().toLowerCase().contains(query)) {
// DataItem's name contains the search query
return true;
} else {
// Otherwise check if any of the search terms match those in the DataItem's keywords
// We split the query by space so we can match DataItems with multiple keywords
String[] searchTerms = query.split(" ");
boolean match = false;
for (String searchTerm : searchTerms) {
match = dataItem.getKeywordsString().toLowerCase().contains(searchTerm);
}
return match;
}
}));
// Wrap the filtered list in a SortedList
SortedList<DataItem> sortedList = new SortedList<>(filteredList);
// Update the ListView
dataItemListView.setItems(sortedList);
}
/**
* Generates a list of sample products
*/
private List<DataItem> buildDataItems() {
List<DataItem> dataItems = new ArrayList<>();
dataItems.add(new DataItem(
1, "School Supplies", "Learn things.",
Arrays.asList("pens", "pencils", "paper", "eraser")));
dataItems.add(new DataItem(
2, "Computer", "Do some things",
Arrays.asList("paper", "cpu", "keyboard", "monitor")));
dataItems.add(new DataItem(
3, "Keyboard", "Type things",
Arrays.asList("keys", "numpad", "input")));
dataItems.add(new DataItem(
4, "Printer", "Print things.",
Arrays.asList("paper", "ink", "computer")));
dataItems.add(new DataItem(
5, "Paper", "Report things.",
Arrays.asList("write", "printer", "notebook")));
return dataItems;
}
}