Как работать с JavaFx TableView с пользовательской ячейкой таблицы, используя редактируемые ячейки Combobox
Я хотел бы создать таблицу со следующими функциями
- Редактировать при нажатии клавиши
- Клавиша ввода = следующая строка
- Клавиша Tab = следующий столбец
- Клавиша Escape = Отменить редактирование
Окно выглядит следующим образом
Основная проблема, которая возникает, когда мы фокусируемся на поле со списком, фокус перемещается за пределы таблицы. Мне нужно решение, при котором фокус остается в таблице или на следующей ячейке таблицы и фиксирует выбранное значение поля со списком. Поле со списком имеет тип Item.
ComboBox<Item> items = new ComboBox<>();
Таблица выглядит следующим образом
public TableView<Purchase> _purchase_table;
public TableColumn<Purchase, String> _id;
public TableColumn<Purchase, Item> _name_of_item; //Of Type Purchase,Item
public TableColumn<Purchase, String> _quantity;
public TableColumn<Purchase, String> _rate;
public TableColumn<Purchase, String> _per;
public TableColumn<Purchase, String> _amount;
Класс покупки выглядит следующим образом
public class Purchase {
private SimpleStringProperty serialNumber;
private SimpleObjectProperty<Item> itemName;
private SimpleStringProperty quantity;
private SimpleStringProperty rate;
private SimpleStringProperty per;
private SimpleStringProperty amount;
public Purchase() {
serialNumber = new SimpleStringProperty();
itemName = new SimpleObjectProperty<>();
quantity = new SimpleStringProperty();
rate = new SimpleStringProperty();
per = new SimpleStringProperty();
amount = new SimpleStringProperty();
}
public String getSerialNumber() {
return serialNumber.get();
}
public SimpleStringProperty serialNumberProperty() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber.set(serialNumber);
}
public Item getItemName() {
return itemName.get();
}
public SimpleObjectProperty<Item> itemNameProperty() {
return itemName;
}
public void setItemName(Item itemName) {
this.itemName.set(itemName);
}
public String getQuantity() {
return quantity.get();
}
public SimpleStringProperty quantityProperty() {
return quantity;
}
public void setQuantity(String quantity) {
this.quantity.set(quantity);
}
public String getRate() {
return rate.get();
}
public SimpleStringProperty rateProperty() {
return rate;
}
public void setRate(String rate) {
this.rate.set(rate);
}
public String getPer() {
return per.get();
}
public SimpleStringProperty perProperty() {
return per;
}
public void setPer(String per) {
this.per.set(per);
}
public String getAmount() {
return amount.get();
}
public SimpleStringProperty amountProperty() {
return amount;
}
public void setAmount(String amount) {
this.amount.set(amount);
}
}
Класс Item выглядит следующим образом
public class Item {
private SimpleStringProperty id;
private SimpleStringProperty itemName;
private SimpleStringProperty stockGroupID;
private SimpleStringProperty stockGroup;
private SimpleStringProperty unitID;
private SimpleStringProperty unitName;
private SimpleStringProperty taxabilityID;
private SimpleStringProperty taxability;
private SimpleStringProperty hsn;
private SimpleStringProperty itemDescription;
private SimpleStringProperty integratedTax;
private SimpleStringProperty centralTax;
private SimpleStringProperty stateTax;
private SimpleStringProperty cess;
public Item() {
id = new SimpleStringProperty();
itemName = new SimpleStringProperty();
stockGroupID = new SimpleStringProperty();
stockGroup = new SimpleStringProperty();
unitID = new SimpleStringProperty();
unitName = new SimpleStringProperty();
taxabilityID = new SimpleStringProperty();
taxability = new SimpleStringProperty();
hsn = new SimpleStringProperty();
itemDescription = new SimpleStringProperty();
integratedTax = new SimpleStringProperty();
centralTax = new SimpleStringProperty();
stateTax = new SimpleStringProperty();
cess = new SimpleStringProperty();
}
public Item(String id,String itemName,String stockGroupID,String stockGroup,String unitID,String unitName,String taxabilityID,
String taxability,String hsn,String itemDescription,String integratedTax,String centralTax,
String stateTax,String cess) {
this.id = new SimpleStringProperty(id);
this.itemName = new SimpleStringProperty(itemName);
this.stockGroupID = new SimpleStringProperty(stockGroupID);
this.stockGroup = new SimpleStringProperty(stockGroup);
this.unitID = new SimpleStringProperty(unitID);
this.unitName = new SimpleStringProperty(unitName);
this.taxabilityID = new SimpleStringProperty(taxabilityID);
this.taxability = new SimpleStringProperty(taxability);
this.hsn = new SimpleStringProperty(hsn);
this.itemDescription = new SimpleStringProperty(itemDescription);
this.integratedTax = new SimpleStringProperty(integratedTax);
this.centralTax = new SimpleStringProperty(centralTax);
this.stateTax = new SimpleStringProperty(stateTax);
this.cess = new SimpleStringProperty(cess);
}
public String getId() {
return id.get();
}
public SimpleStringProperty idProperty() {
return id;
}
public void setId(String id) {
this.id.set(id);
}
public String getItemName() {
return itemName.get();
}
public SimpleStringProperty itemNameProperty() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName.set(itemName);
}
public String getStockGroupID() {
return stockGroupID.get();
}
public SimpleStringProperty stockGroupIDProperty() {
return stockGroupID;
}
public void setStockGroupID(String stockGroupID) {
this.stockGroupID.set(stockGroupID);
}
public String getStockGroup() {
return stockGroup.get();
}
public SimpleStringProperty stockGroupProperty() {
return stockGroup;
}
public void setStockGroup(String stockGroup) {
this.stockGroup.set(stockGroup);
}
public String getUnitID() {
return unitID.get();
}
public SimpleStringProperty unitIDProperty() {
return unitID;
}
public void setUnitID(String unitID) {
this.unitID.set(unitID);
}
public String getUnitName() {
return unitName.get();
}
public SimpleStringProperty unitNameProperty() {
return unitName;
}
public void setUnitName(String unitName) {
this.unitName.set(unitName);
}
public String getTaxabilityID() {
return taxabilityID.get();
}
public SimpleStringProperty taxabilityIDProperty() {
return taxabilityID;
}
public void setTaxabilityID(String taxabilityID) {
this.taxabilityID.set(taxabilityID);
}
public String getTaxability() {
return taxability.get();
}
public SimpleStringProperty taxabilityProperty() {
return taxability;
}
public void setTaxability(String taxability) {
this.taxability.set(taxability);
}
public String getHsn() {
return hsn.get();
}
public SimpleStringProperty hsnProperty() {
return hsn;
}
public void setHsn(String hsn) {
this.hsn.set(hsn);
}
public String getItemDescription() {
return itemDescription.get();
}
public SimpleStringProperty itemDescriptionProperty() {
return itemDescription;
}
public void setItemDescription(String itemDescription) {
this.itemDescription.set(itemDescription);
}
public String getIntegratedTax() {
return integratedTax.get();
}
public SimpleStringProperty integratedTaxProperty() {
return integratedTax;
}
public void setIntegratedTax(String integratedTax) {
this.integratedTax.set(integratedTax);
}
public String getCentralTax() {
return centralTax.get();
}
public SimpleStringProperty centralTaxProperty() {
return centralTax;
}
public void setCentralTax(String centralTax) {
this.centralTax.set(centralTax);
}
public String getStateTax() {
return stateTax.get();
}
public SimpleStringProperty stateTaxProperty() {
return stateTax;
}
public void setStateTax(String stateTax) {
this.stateTax.set(stateTax);
}
public String getCess() {
return cess.get();
}
public SimpleStringProperty cessProperty() {
return cess;
}
public void setCess(String cess) {
this.cess.set(cess);
}
}
Класс модели покупки
public class PurchaseModel {
private ObservableList<Purchase> purchases;
private ObservableList<Item> items;
private int row,column;
public PurchaseModel() {
row = 0;
column = 0;
purchases = FXCollections.observableArrayList();
Purchase purchase = new Purchase();
purchase.setSerialNumber("1");
purchase.setItemName(getItem());
purchase.setQuantity("4");
purchase.setRate("20.00");
purchase.setPer("Pcs");
purchase.setAmount("80.00");
purchases.add(purchase);
items = FXCollections.observableArrayList();
Item item = new Item();
item.setId("1");
item.setItemName("Lux");
item.setStockGroupID("1");
item.setStockGroup("items @ 5%");
item.setUnitID("1");
item.setUnitName("Kg");
item.setTaxabilityID("1");
item.setTaxability("5%");
item.setHsn("344344");
item.setItemDescription("dkfmks skmd");
item.setIntegratedTax("5");
item.setCentralTax("2.5");
item.setStateTax("2.5");
item.setCess("0.0");
items.add(item);
item = new Item();
item.setId("2");
item.setItemName("Godrej");
item.setStockGroupID("1");
item.setStockGroup("items @ 5%");
item.setUnitID("1");
item.setUnitName("Kg");
item.setTaxabilityID("1");
item.setTaxability("5%");
item.setHsn("344344");
item.setItemDescription("dkfmks skmd");
item.setIntegratedTax("5");
item.setCentralTax("2.5");
item.setStateTax("2.5");
item.setCess("0.0");
items.add(item);
item = new Item();
item.setId("2");
item.setItemName("Pears");
item.setStockGroupID("1");
item.setStockGroup("items @ 5%");
item.setUnitID("1");
item.setUnitName("Kg");
item.setTaxabilityID("1");
item.setTaxability("5%");
item.setHsn("344344");
item.setItemDescription("dkfmks skmd");
item.setIntegratedTax("5");
item.setCentralTax("2.5");
item.setStateTax("2.5");
item.setCess("0.0");
items.add(item);
}
public ObservableList<Purchase> fillPurchaseTableViewWithData(){
return purchases;
}
public ObservableList<Item> fillItemListViewWithData(){
return items;
}
public Item getItem(){
return new Item("1","Porter and Gamble","1","Item @ 5%","1","Kg",
"1","5%","23323","a very good toy","5","2.5",
"2.5","0");
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getColumn() {
return column;
}
public void setColumn(int column) {
this.column = column;
}
}
Класс контроллера выглядит следующим образом
public class PurchaseController implements Initializable {
public TableView<Purchase> _purchase_table;
public TableColumn<Purchase, String> _id;
public TableColumn<Purchase, Item> _name_of_item;
public TableColumn<Purchase, String> _quantity;
public TableColumn<Purchase, String> _rate;
public TableColumn<Purchase, String> _per;
public TableColumn<Purchase, String> _amount;
private PurchaseModel theModel;
@Override
public void initialize(URL location, ResourceBundle resources) {
theModel = new PurchaseModel();
_id.setCellValueFactory(new IDCellValueFactory());
_name_of_item.setCellValueFactory(new ItemCellValueFactory());
_quantity.setCellValueFactory(new QuantityCellValueFactory());
_rate.setCellValueFactory(new RateCellValueFactory());
_per.setCellValueFactory(new PerCellValueFactory());
_amount.setCellValueFactory(new AmountCellValueFactory());
_name_of_item.setCellFactory(new ItemComboBoxEditingCellFactory());
//_name_of_item.setOnEditCommit(new ItemCellOnEditCommit() );
_purchase_table.setItems(theModel.fillPurchaseTableViewWithData());
_purchase_table.setEditable(true);
_purchase_table.getSelectionModel().cellSelectionEnabledProperty().set(true);
_purchase_table.getFocusModel().focusedCellProperty().addListener(new PurchaseTableCellSelectionListener());
}
private class IDCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, String>, ObservableValue<String>> {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Purchase, String> param) {
return param.getValue().serialNumberProperty();
}
}
private class ItemCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, Item>, ObservableValue<Item>> {
@Override
public ObservableValue<Item> call(TableColumn.CellDataFeatures<Purchase, Item> param) {
return param.getValue().itemNameProperty();
}
}
private class QuantityCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, String>, ObservableValue<String>> {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Purchase, String> param) {
return param.getValue().quantityProperty();
}
}
private class RateCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, String>, ObservableValue<String>> {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Purchase, String> param) {
return param.getValue().rateProperty();
}
}
private class PerCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, String>, ObservableValue<String>> {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Purchase, String> param) {
return param.getValue().perProperty();
}
}
private class AmountCellValueFactory implements Callback<TableColumn.CellDataFeatures<Purchase, String>, ObservableValue<String>> {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Purchase, String> param) {
return param.getValue().amountProperty();
}
}
private class ItemComboBoxEditingCellFactory implements Callback<TableColumn<Purchase, Item>, TableCell<Purchase, Item>> {
@Override
public TableCell<Purchase, Item> call(TableColumn<Purchase, Item> param) {
return new ItemComboBoxEditingCell();
}
}
private class ItemComboBoxEditingCell extends TableCell<Purchase, Item> {
private ComboBox<Item> comboBox;
private TablePosition<Purchase,Item> pos;
public ItemComboBoxEditingCell() {
pos = new TablePosition<>(getTableView(),0,null);
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createComboBox();
setText(null);
setGraphic(comboBox);
}
}
@Override
public void commitEdit(Item newValue) {
super.commitEdit(newValue);
((Purchase)getTableView().getItems()
.get(getIndex()))
.setItemName(newValue);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItemSelected().getItemName());
setGraphic(null);
}
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty){
setGraphic(null);
setText(null);
}else {
setText(getItemSelected().getItemName());
setGraphic(comboBox);
}
}
private Item getItemSelected() {
return getItem() == null ? theModel.getItem() : getItem();
}
private void createComboBox() {
comboBox = new ComboBox<>();
comboBox.setItems(theModel.fillItemListViewWithData());
comboBox.setEditable(true);
comboBox.setCellFactory(new ItemComboBoxCellFactory());
//comboBox.setOnAction(new ItemComboBoxSelectionListener());
//comboBox.focusedProperty().addListener(new ItemComboBoxFocusListener());
//comboBox.showingProperty().addListener(new ItemComboBoxShowingListener());
comboBox.valueProperty().set(getItemSelected());
comboBox.setConverter(new ItemComboBoxStringConverter());
comboBox.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
}
private class ItemComboBoxCellFactory implements Callback<ListView<Item>,ListCell<Item>>{
@Override
public ListCell<Item> call(ListView<Item> param) {
return new ItemComboBoxCell();
}
}
private class ItemComboBoxCell extends ListCell<Item>{
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty){
setText(null);
}else {
setText(item.getItemName());
}
}
}
private class ItemComboBoxStringConverter extends StringConverter<Item>{
private Item i;
@Override
public String toString(Item item) {
if (item == null)
return null;
i = item;
return item.getItemName();
}
@Override
public Item fromString(String s) {
return i;
}
}
}
private class PurchaseTableCellSelectionListener implements ChangeListener<TablePosition>{
@Override
public void changed(ObservableValue<? extends TablePosition> observableValue, TablePosition tablePosition, TablePosition t1) {
int row = t1.getRow();
int column = t1.getColumn();
theModel.setRow(row);
theModel.setColumn(column);
}
}
Подскажите, пожалуйста, как реализовать все четыре пункта, которые я упомянул выше.