У меня два ComboBox. В первый ComboBox я добавляю доступных клиентов из базы данных. Я добавляю список во второй ComboBox на основе выбора первого ComboBox. Я хочу, чтобы ComboBox не редактировался, я показываю текст, набранный во всплывающей подсказке над ComboBox. На основе набранных символов ComboBox должен фильтровать элементы.
Я пробовал это, но когда я набираю текст или символы, список ComboBox становится невидимым и не дает мне вывода, как я хотел. Я не знаю, что делаю неправильно, пожалуйста, помогите.
Класс контроллера -
public class FinanceActionsController implements Initializable {
@FXML
private ComboBox<BillingPartyOptions> cmbBillingParty;
@FXML
private ComboBox<AddressOptions> cmbAddress;
final ObservableList<BillingPartyOptions> billPartyOptions = FXCollections.observableArrayList();
final ObservableList<AddressOptions> addressOptions = FXCollections.observableArrayList();
final Connection con = new DBConnection().getConnection();
@Override
public void initialize(URL url, ResourceBundle rb) {
new ComboBoxAutoComplete<BillingPartyOptions>(cmbBillingParty);
}
@FXML
private void onClickBtnAdd(ActionEvent event) {
enableOnClickAdd();
populateCmbBillingParty();
}
private void populateCmbBillingParty() {
try {
cmbBillingParty.setItems(billPartyOptions);
cmbAddress.setItems(addressOptions);
PreparedStatement ps = null;
ResultSet rs = null;
ps = con.prepareStatement("SELECT CUSTOMER_ID, CUSTOMER_NAME FROM CUSTOMERS WHERE CUSTOMER_STATUS = 'Y'");
rs = ps.executeQuery();
while (rs.next()) {
billPartyOptions.add(new BillingPartyOptions(rs.getInt("CUSTOMER_ID"), rs.getString("CUSTOMER_NAME")));
}
cmbBillingParty.setConverter(new StringConverter<BillingPartyOptions>() {
@Override
public String toString(BillingPartyOptions object) {
return object.getCustomerName();
}
@Override
public BillingPartyOptions fromString(String string) {
return null;
}
});
cmbBillingParty.valueProperty().addListener((obs, oldVal, newVal) -> {
addressOptions.clear();
try {
int customerID;
if (newVal.getCustomerId() == null) {
customerID = 0;
} else {
customerID = newVal.getCustomerId();
}
PreparedStatement ps1 = con.prepareCall("SELECT 0 AS ADDRESS_ID, CM.ADDRESS, CM.CUSTOMER_ID, CM.STATE_ID FROM CUSTOMERS CM LEFT JOIN ADDRESSES AM ON CM.CUSTOMER_ID = AM.CUSTOMER_ID WHERE CM.CUSTOMER_ID = ? UNION ALL SELECT AM.ADDRESS_ID, AM.ADDRESS, AM.CUSTOMER_ID, AM.STATE_ID FROM ADDRESSES AM INNER JOIN CUSTOMERS CM ON AM.CUSTOMER_ID = CM.CUSTOMER_ID WHERE CM.CUSTOMER_ID = ?");
ps1.setInt(1, customerID);
ps1.setInt(2, customerID);
ResultSet rs1 = ps1.executeQuery();
while (rs1.next()) {
addressOptions.add(new AddressOptions(rs1.getInt(1), rs1.getString(2), rs1.getInt(3), rs1.getInt(4)));
}
cmbAddress.setConverter(new StringConverter<AddressOptions>() {
@Override
public String toString(AddressOptions object) {
return object.getAddress();
}
@Override
public AddressOptions fromString(String string) {
return null;
}
});
} catch (NullPointerException ne) {
clearComboBoxes();
} catch (Exception ex) {
clearComboBoxes();
System.out.println(ex);
}
});
ps.close();
rs.close();
//con.close();
} catch (Exception e) {
System.out.println(e.getCause());
}
}
Я добавляю объекты класса в список наблюдаемых.
class BillingPartyOptions {
private Integer customerId;
private String customerName;
public BillingPartyOptions(Integer customerId, String customerName) {
this.customerId = customerId;
this.customerName = customerName;
}
public Integer getCustomerId() {
return customerId;
}
public String getCustomerName() {
return customerName;
}
}
class AddressOptions {
private Integer addressId;
private String address;
private Integer customerId;
private Integer stateId;
public AddressOptions(Integer addressId, String address, Integer customerId, Integer stateId) {
this.addressId = addressId;
this.address = address;
this.customerId = customerId;
this.stateId = stateId;
}
public Integer getAddressId() {
return addressId;
}
public String getAddress() {
return address;
}
public Integer getStateId() {
return stateId;
}
public Integer getCustomerId() {
return customerId;
}
}
Я использую этот класс ниже для фильтра автозаполнения с подсказкой
package controllers;
import java.util.stream.Stream;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Window;
public class ComboBoxAutoComplete<T> {
private ComboBox<T> cmb;
String filter = "";
private ObservableList<T> originalItems;
public ComboBoxAutoComplete(ComboBox<T> cmb) {
this.cmb = cmb;
originalItems = FXCollections.observableArrayList(cmb.getItems());
cmb.setTooltip(new Tooltip());
cmb.setOnKeyPressed(this::handleOnKeyPressed);
cmb.setOnHidden(this::handleOnHiding);
}
public void handleOnKeyPressed(KeyEvent e) {
ObservableList<T> filteredList = FXCollections.observableArrayList();
KeyCode code = e.getCode();
if (code.isLetterKey()) {
filter += e.getText();
}
if (code == KeyCode.BACK_SPACE && filter.length() > 0) {
filter = filter.substring(0, filter.length() - 1);
cmb.getItems().setAll(originalItems);
}
if (code == KeyCode.ESCAPE) {
filter = "";
}
if (filter.length() == 0) {
filteredList = originalItems;
cmb.getTooltip().hide();
} else {
Stream<T> itens = cmb.getItems().stream();
String txtUsr = filter.toString().toLowerCase();
itens.filter(el -> el.toString().toLowerCase().contains(txtUsr)).forEach(filteredList::add);
cmb.getTooltip().setText(txtUsr);
Window stage = cmb.getScene().getWindow();
double posX = stage.getX() + cmb.getBoundsInParent().getMinX();
double posY = stage.getY() + cmb.getBoundsInParent().getMinY();
cmb.getTooltip().show(stage, posX, posY);
cmb.show();
}
cmb.getItems().setAll(filteredList);
}
public void handleOnHiding(Event e) {
filter = "";
cmb.getTooltip().hide();
T s = cmb.getSelectionModel().getSelectedItem();
cmb.getItems().setAll(originalItems);
cmb.getSelectionModel().select(s);
}
}
Извините за добавление длинного кода.