Вот черновой пример, в котором поиск работает по логике AND, т. Е. Все поисковые тексты учитываются.
Предположим, у вас есть люди в качестве модели данных:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import javafx.beans.property.SimpleStringProperty;
class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
String getFirstName() {
return firstName.get();
}
String getLastName() {
return lastName.get();
}
String getEmail() {
return email.get();
}
}
Стол выполнен в виде:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051.MyCellValueFactory?>
<AnchorPane prefHeight="400.0"
prefWidth="600.0"
xmlns="http://javafx.com/javafx/8.0.171"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051.Controller">
<TableView fx:id="tableView" prefWidth="600">
<columnResizePolicy>
<TableView fx:constant="UNCONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn text="First Name">
<cellValueFactory>
<MyCellValueFactory property="firstName"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name">
<cellValueFactory>
<MyCellValueFactory property="lastName"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="E-mail">
<cellValueFactory>
<MyCellValueFactory property="email"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</AnchorPane>
У нас есть пользовательские классы строк таблицы.
MyTableRowData:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
public abstract class MyTableRowData<T> {
public abstract T firstNameProperty();
public abstract T lastNameProperty();
public abstract T emailProperty();
}
SearchTableRowData:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class SearchTableRowData extends MyTableRowData<ObjectProperty<String>> {
private final ObjectProperty<String> firstName = new SimpleObjectProperty<>();
private final ObjectProperty<String> lastName = new SimpleObjectProperty<>();
private final ObjectProperty<String> email = new SimpleObjectProperty<>();
@Override
public ObjectProperty<String> firstNameProperty() {
return firstName;
}
@Override
public ObjectProperty<String> lastNameProperty() {
return lastName;
}
@Override
public ObjectProperty<String> emailProperty() {
return email;
}
}
PersonTableRowData:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
class PersonTableRowData extends MyTableRowData<ObjectProperty<String>> {
private final ObjectProperty<String> firstName;
private final ObjectProperty<String> lastName;
private final ObjectProperty<String> email;
PersonTableRowData(final Person person) {
this.firstName = new SimpleObjectProperty<>(person.getFirstName());
this.lastName = new SimpleObjectProperty<>(person.getLastName());
this.email = new SimpleObjectProperty<>(person.getEmail());
}
@Override
public ObjectProperty<String> firstNameProperty() {
return firstName;
}
@Override
public ObjectProperty<String> lastNameProperty() {
return lastName;
}
@Override
public ObjectProperty<String> emailProperty() {
return email;
}
}
Класс MyCellValueFactory - это сердце всей магии:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import java.lang.reflect.Method;
import javafx.beans.NamedArg;
import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.text.Text;
import javafx.util.Callback;
public class MyCellValueFactory<P extends MyTableRowData<?>, S extends Node>
implements Callback<TableColumn.CellDataFeatures<P, S>, ObservableValue<S>> {
private final String property;
public MyCellValueFactory(@NamedArg("property") final String property) {
this.property = property;
}
@Override
public ObservableValue<S> call(final TableColumn.CellDataFeatures<P, S> param) {
final P tableRowData = param.getValue();
if (tableRowData instanceof SearchTableRowData) {
return new SimpleObjectProperty<>(buildSearchTextBox(tableRowData));
} else if (tableRowData instanceof PersonTableRowData) {
return new SimpleObjectProperty<>(buildText(tableRowData));
}
return new SimpleObjectProperty<>();
}
@SuppressWarnings("unchecked")
private S buildText(final P tableRowData) {
final Text text = new Text();
text.textProperty().bind(extractProperty(tableRowData));
return (S) text;
}
@SuppressWarnings("unchecked")
private S buildSearchTextBox(final P tableRowData) {
final TextField searchTextField = new TextField();
searchTextField.promptTextProperty().set(property);
searchTextField.textProperty().bindBidirectional(extractProperty(tableRowData));
return (S) searchTextField;
}
@SuppressWarnings("unchecked")
private <T extends Observable> T extractProperty(final P tableRowData) {
try {
final Class<?> c = Class.forName(tableRowData.getClass().getName());
final Method method = c.getDeclaredMethod(property + "Property");
return (T) method.invoke(tableRowData);
} catch (final Exception exc) {
throw new RuntimeException(exc);
}
}
}
Контроллер Класс:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import org.apache.commons.lang3.StringUtils;
public class Controller {
@FXML
private TableView<MyTableRowData<?>> tableView;
private final ObservableValue<? extends ObservableList<MyTableRowData<?>>> observableRows =
new SimpleListProperty<>(FXCollections.observableArrayList());
private final SearchTableRowData searchTableRowData = new SearchTableRowData();
private final List<Person> originalPersons = Arrays.asList(
new Person("A", "B", "c@c.com"),
new Person("AA", "BB", "cc@c.com"),
new Person("AAA", "BBB", "ccc@c.com"),
new Person("AAAA", "BBBB", "cccc@c.com"));
@FXML
void initialize() {
tableView.itemsProperty().bind(observableRows);
observableRows.getValue().add(searchTableRowData);
searchTableRowData.firstNameProperty().addListener((o, oldValue, newValue) -> fillPersons());
searchTableRowData.lastNameProperty().addListener((o, oldValue, newValue) -> fillPersons());
searchTableRowData.emailProperty().addListener((o, oldValue, newValue) -> fillPersons());
fillPersons();
}
private void fillPersons() {
((ObservableList) observableRows).remove(1, ((ObservableList) observableRows).size());
observableRows
.getValue()
.addAll(originalPersons
.stream()
.filter(getFirstNamePredicate())
.filter(getLastNamePredicate())
.filter(getEmailPredicate())
.map(PersonTableRowData::new)
.collect(Collectors.toList()));
}
private Predicate<Person> getFirstNamePredicate() {
final String value = searchTableRowData.firstNameProperty().get();
return StringUtils.isNoneEmpty(value) ?
person -> StringUtils.containsIgnoreCase(person.getFirstName(), value) :
person -> true;
}
private Predicate<Person> getLastNamePredicate() {
final String value = searchTableRowData.lastNameProperty().get();
return StringUtils.isNoneEmpty(value) ?
person -> StringUtils.containsIgnoreCase(person.getLastName(), value) :
person -> true;
}
private Predicate<Person> getEmailPredicate() {
final String value = searchTableRowData.emailProperty().get();
return StringUtils.isNoneEmpty(value) ?
person -> StringUtils.containsIgnoreCase(person.getEmail(), value) :
person -> true;
}
}
Наконец-то вы запускаете все вместе этим Main классом:
package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;
import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.dmaslenko.stackexchange.stackoverflow.q54033646.CsvReader;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
@SuppressWarnings("Duplicates")
public void start(final Stage stage) throws Exception {
final URL fxmlFileResource =
CsvReader.class.getResource("/com/dmaslenko/stackexchange/stackoverflow/javafx/q54280051/main.fxml");
final FXMLLoader loader = new FXMLLoader(fxmlFileResource);
final Parent rootPane = loader.load();
loader.getController();
final Scene scene = new Scene(rootPane, 600, 400);
stage.setScene(scene);
stage.show();
}
}
Это черновой, но рабочий пример, без особой оптимизации.
Известные области для улучшения:
- любое изменение в полях поиска теряет фокус и курсор.
- первый ряд выбирается, но не должен.