JavaFX8 - 定制的 EditCell class - 如何禁用 TableView 的鼠标点击,同时仍然允许在正在编辑的 TableCell 内点击?
JavaFX8 - customised EditCell class - how to disable mouse clicks for a TableView while still allowing clicks inside a TableCell that's being edited?
我正在编写一个将有很多数据输入的应用程序 windows,每个数据输入都有一个可编辑的 TableView。
我正在使用用户 James_D 非常有用的 EditCell 代码 https://gist.github.com/james-d/be5bbd6255a4640a5357 作为 TableCell 编辑的基础,并将其扩展为包括数字和日期数据类型。
我正在验证输入的数据。如果没有错误,我会让用户通过单击另一个单元格或通过按 Tab 键或按 Shift+Tab 键移出单元格来离开该单元格。如果有错误,我不会让用户离开单元格,直到错误被更正。
我的代码与一件事分开工作。
我在 TableView 上使用鼠标事件处理程序来检测用户何时试图单击离开单元格。如果出现错误,我会使用点击来阻止焦点离开单元格。
那部分工作正常。
但是,处理程序也会消耗点击 inside 正在编辑的单元格,因此如果用户想要点击以将光标定位在相关位置以更正错误,他们可以't.
有没有办法解决这个问题。允许在正在编辑的单元格内单击,同时在表格视图级别禁用单击?
我也尝试过在 TableView 级别使用 setMouseTransparent 而不是消耗点击次数,但同样的事情发生了。
这是我的代码的摘录。
我在 FXML 控制器超类型中声明了鼠标事件处理程序。 dataEntryError 标志在应用级别声明。
import static ztestform.ZTestForm.dataEntryError;
//...
public static EventHandler<MouseEvent> tvMousePressedHandler;
//...
public void defineMouseEventHandler() {
tvMousePressedHandler = (MouseEvent event) -> {
if ( dataEntryError ) event.consume();
};
}
我将鼠标事件处理程序添加到每个相关 FXML 控制器中的 TableView。
import static ztestform.ZTestForm.dataEntryError;
//...
private void initialiseTableView() {
//...
defineMouseEventHandler();
tvTestModel.addEventFilter(MouseEvent.MOUSE_PRESSED, tvMousePressedHandler);
//...
}
根据 James_D 的示例,我有一个 EditCell class,它由 TableColumns 上的单元格工厂实例化。它管理单元格中的开始编辑以及根据需要提交或取消编辑。
在 EditCell 中,我使用单元格的 textProperty() 上的更改侦听器捕获数据输入错误。如果出现错误,我会添加 TableView 鼠标事件处理程序来处理单击。如果没有错误,则删除鼠标事件处理程序。
textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
getTableView().removeEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
if ( isDataValid(classType, newValue) ) {
dataEntryError = false;
textField.setStyle("");
DAOGenUtil.clearSystemMessage(controllerRef);
} else {
dataEntryError = true;
textField.setStyle("-fx-background-color: pink;");
displayErrorMessage(classType, controllerRef);
getTableView().addEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
}
});
在这里的某个地方我想说 "allow clicks in textField even though they're consumed at the TableView level" 但不知道该怎么做。
有人能帮帮我吗?我已经坚持了四天了。 :-(
我正在使用 JavaFX8、NetBeans 8.2 和 Scene Builder 8.3。
如果您需要查看,这里是我扩展的 EditCell class 的完整代码。感谢 James_D 发布原始代码,也感谢 Whosebug 上非常聪明的人回答问题;这些解决方案对我来说是宝贵的知识来源!
package ztestform;
import fxmlcontrollers.FXMLControllerSuperType;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.util.StringConverter;
import javafx.util.converter.DoubleStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LongStringConverter;
import static ztestform.ZTestForm.COLOUR_DARK_RED;
import static ztestform.ZTestForm.FORMAT_DATE_DISPLAY;
import static ztestform.ZTestForm.FORMAT_DOUBLE_FOUR_DECIMALS;
import static ztestform.ZTestForm.FORMAT_INTEGER;
import static ztestform.ZTestForm.dataEntryError;
public class EditCell<S, T> extends TableCell<S, T> {
private final TextField textField = new TextField();
//Converter for converting the text in the text field to the user type, and vice-versa:
private final StringConverter<T> converter ;
public static DAOGenUtil DAOGenUtil = new DAOGenUtil();
private final Class<T> classType = null;
public EditCell(StringConverter<T> converter, Class<T> classType, FXMLControllerSuperType controllerRef) {
this.converter = converter ;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
//*******************************************************************************************************************
//If the user hits ENTER and there are no data entry errors, commit the edit
textField.setOnAction(evt -> {
if ( ! dataEntryError ) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
//*******************************************************************************************************************
//If the cell loses focus and there are no data entry errors, commit the edit
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused && ! dataEntryError ) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
//*******************************************************************************************************************
//Validate data as it's entered
textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
getTableView().removeEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
if ( isDataValid(classType, newValue) ) {
dataEntryError = false;
textField.setStyle("");
DAOGenUtil.clearSystemMessage(controllerRef);
} else {
dataEntryError = true;
textField.setStyle("-fx-background-color: pink;");
displayErrorMessage(classType, controllerRef);
getTableView().addEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
}
});
//*******************************************************************************************************************
//Trap and process ESCAPE, TAB and SHIFT+TAB
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
TablePosition<S, ?> pos = getTableView().getFocusModel().getFocusedCell();
int maximumVisibleColumnNumber = DAOGenUtil.getMaximumVisibleColumnNumber(getTableView());
if (event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
dataEntryError = false;
DAOGenUtil.clearSystemMessage(controllerRef);
getTableView().setMouseTransparent(false);
} else if ( event.isShiftDown() && event.getCode() == KeyCode.TAB ) {
if ( dataEntryError ) {
event.consume();
} else {
getTableView().setMouseTransparent(false);
getTableView().getSelectionModel().selectLeftCell();
if ( pos.getColumn() == 0 ) {
//We're at the start of a row so position to the end of the previous row
getTableView().getSelectionModel().select(pos.getRow()-1, getTableView().getColumns().get(maximumVisibleColumnNumber));
event.consume();
}
}
} else if ( event.getCode() == KeyCode.TAB ) {
if ( dataEntryError ) {
event.consume();
} else {
getTableView().setMouseTransparent(false);
getTableView().getSelectionModel().selectRightCell();
if ( pos.getColumn() == maximumVisibleColumnNumber ) {
//We're at the end of a row so position to the start of the next row
getTableView().getSelectionModel().select(pos.getRow()+1, getTableView().getColumns().get(0));
event.consume();
}
}
}
});
}
//*******************************************************************************************************************
//Create EditCells for String data types
public static final StringConverter<String> IDENTITY_CONVERTER_STRING = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
public static <S> EditCell<S, String> createStringEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, String>(IDENTITY_CONVERTER_STRING, String.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for Long data types
public static final LongStringConverter IDENTITY_CONVERTER_LONG = new LongStringConverter() {
public String toString(Long object) {
String object2 = DAOGenUtil.formatValue(FORMAT_INTEGER, Long.toString(object));
return ( object == null ? "0" : object2 );
}
public Long fromString(String object) {
Long object3 = Long.parseLong(object.replaceAll(",",""));
return ( object.isEmpty() ? 0 : object3 );
}
};
public static <S> EditCell<S, Long> createLongEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, Long>(IDENTITY_CONVERTER_LONG, Long.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for Double data types
public static final DoubleStringConverter IDENTITY_CONVERTER_DOUBLE = new DoubleStringConverter() {
public String toString(Double object) {
String object2 = DAOGenUtil.formatValue(FORMAT_DOUBLE_FOUR_DECIMALS, Double.toString(object));
return ( object == null ? "0" : object2 );
}
public Double fromString(String object) {
Double object3 = Double.parseDouble(object.replaceAll(",",""));
return ( object.isEmpty() ? 0 : object3 );
}
};
public static <S> EditCell<S, Double> createDoubleEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, Double>(IDENTITY_CONVERTER_DOUBLE, Double.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for LocalDate data types
public static final LocalDateStringConverter IDENTITY_CONVERTER_LOCAL_DATE = new LocalDateStringConverter() {
public String toString(LocalDate object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(FORMAT_DATE_DISPLAY);
String object2 = dateFormatter.format( (TemporalAccessor) object);
return ( object == null ? "0" : object2 );
}
public LocalDate fromString(String object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/M/yyyy");
LocalDate object3 = LocalDate.parse(object, dateFormatter);
return object.isEmpty() ? null : object3;
}
};
public static <S> EditCell<S, LocalDate> createLocalDateEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, LocalDate>(IDENTITY_CONVERTER_LOCAL_DATE, LocalDate.class, controllerRef);
}
//*******************************************************************************************************************
//Code to start, cancel and commit edits
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (! isEditing() && ! item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TablePosition<S,T>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
//*******************************************************************************************************************
//Validate data
public boolean isDataValid(Class<T> classType, String enteredData) {
boolean dataOK = true;
String enteredDataWithoutCommas = "";
if ( classType == Long.class || classType == Double.class ) {
enteredDataWithoutCommas = enteredData.replaceAll(",", "");
}
if ( ( classType == Long.class && ! DAOGenUtil.isIntegerOrLong(enteredDataWithoutCommas) )
|| classType == Double.class && ! DAOGenUtil.isNumeric(enteredDataWithoutCommas)
|| classType == LocalDate.class && ! DAOGenUtil.isDate(enteredData) ) {
dataOK = false;
} else {
dataOK = true;
}
return dataOK;
}
//*******************************************************************************************************************
//Display data entry error messages
public void displayErrorMessage(Class<T> classType, FXMLControllerSuperType controllerRef) {
if ( classType == Long.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid number (expected format 9,999).", COLOUR_DARK_RED);
} else if ( classType == Double.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid number (expected format 9,999.9999).", COLOUR_DARK_RED);
} else if ( classType == LocalDate.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid date (expected format DAY/MONTH/4-digit YEAR).", COLOUR_DARK_RED);
}
}
}
在 MouseEvent
中,单击的节点可通过 pickResult
访问。请注意,这可能是皮肤引入的控件的子控件,而不是控件本身。您仍然可以通过向上遍历场景层次结构来找到单击的单元格。这使您可以确定点击是否在正在编辑的单元格之外,并根据以下信息决定是否使用该事件:
public static void registerEditingHandler(final TableView<?> tableView) {
EventHandler<MouseEvent> handler = event -> {
TablePosition<?, ?> position = tableView.getEditingCell();
if (position != null) {
Node n = event.getPickResult().getIntersectedNode();
while (n != tableView
&& !(n instanceof TableCell)) {
n = n.getParent();
}
// consume cells outside of cells or on cells not matching the
// editedPosition
if (n == tableView) {
event.consume();
} else {
TableCell<?, ?> cell = (TableCell<?, ?>) n;
if (cell.getIndex() != position.getRow()
|| cell.getTableColumn() != position.getTableColumn()) {
event.consume();
}
}
}
};
tableView.addEventFilter(MouseEvent.MOUSE_CLICKED, handler);
tableView.addEventFilter(MouseEvent.MOUSE_PRESSED, handler);
tableView.addEventFilter(MouseEvent.MOUSE_RELEASED, handler);
}
我正在编写一个将有很多数据输入的应用程序 windows,每个数据输入都有一个可编辑的 TableView。
我正在使用用户 James_D 非常有用的 EditCell 代码 https://gist.github.com/james-d/be5bbd6255a4640a5357 作为 TableCell 编辑的基础,并将其扩展为包括数字和日期数据类型。
我正在验证输入的数据。如果没有错误,我会让用户通过单击另一个单元格或通过按 Tab 键或按 Shift+Tab 键移出单元格来离开该单元格。如果有错误,我不会让用户离开单元格,直到错误被更正。
我的代码与一件事分开工作。
我在 TableView 上使用鼠标事件处理程序来检测用户何时试图单击离开单元格。如果出现错误,我会使用点击来阻止焦点离开单元格。
那部分工作正常。
但是,处理程序也会消耗点击 inside 正在编辑的单元格,因此如果用户想要点击以将光标定位在相关位置以更正错误,他们可以't.
有没有办法解决这个问题。允许在正在编辑的单元格内单击,同时在表格视图级别禁用单击?
我也尝试过在 TableView 级别使用 setMouseTransparent 而不是消耗点击次数,但同样的事情发生了。
这是我的代码的摘录。
我在 FXML 控制器超类型中声明了鼠标事件处理程序。 dataEntryError 标志在应用级别声明。
import static ztestform.ZTestForm.dataEntryError;
//...
public static EventHandler<MouseEvent> tvMousePressedHandler;
//...
public void defineMouseEventHandler() {
tvMousePressedHandler = (MouseEvent event) -> {
if ( dataEntryError ) event.consume();
};
}
我将鼠标事件处理程序添加到每个相关 FXML 控制器中的 TableView。
import static ztestform.ZTestForm.dataEntryError;
//...
private void initialiseTableView() {
//...
defineMouseEventHandler();
tvTestModel.addEventFilter(MouseEvent.MOUSE_PRESSED, tvMousePressedHandler);
//...
}
根据 James_D 的示例,我有一个 EditCell class,它由 TableColumns 上的单元格工厂实例化。它管理单元格中的开始编辑以及根据需要提交或取消编辑。
在 EditCell 中,我使用单元格的 textProperty() 上的更改侦听器捕获数据输入错误。如果出现错误,我会添加 TableView 鼠标事件处理程序来处理单击。如果没有错误,则删除鼠标事件处理程序。
textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
getTableView().removeEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
if ( isDataValid(classType, newValue) ) {
dataEntryError = false;
textField.setStyle("");
DAOGenUtil.clearSystemMessage(controllerRef);
} else {
dataEntryError = true;
textField.setStyle("-fx-background-color: pink;");
displayErrorMessage(classType, controllerRef);
getTableView().addEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
}
});
在这里的某个地方我想说 "allow clicks in textField even though they're consumed at the TableView level" 但不知道该怎么做。
有人能帮帮我吗?我已经坚持了四天了。 :-(
我正在使用 JavaFX8、NetBeans 8.2 和 Scene Builder 8.3。
如果您需要查看,这里是我扩展的 EditCell class 的完整代码。感谢 James_D 发布原始代码,也感谢 Whosebug 上非常聪明的人回答问题;这些解决方案对我来说是宝贵的知识来源!
package ztestform;
import fxmlcontrollers.FXMLControllerSuperType;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.util.StringConverter;
import javafx.util.converter.DoubleStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LongStringConverter;
import static ztestform.ZTestForm.COLOUR_DARK_RED;
import static ztestform.ZTestForm.FORMAT_DATE_DISPLAY;
import static ztestform.ZTestForm.FORMAT_DOUBLE_FOUR_DECIMALS;
import static ztestform.ZTestForm.FORMAT_INTEGER;
import static ztestform.ZTestForm.dataEntryError;
public class EditCell<S, T> extends TableCell<S, T> {
private final TextField textField = new TextField();
//Converter for converting the text in the text field to the user type, and vice-versa:
private final StringConverter<T> converter ;
public static DAOGenUtil DAOGenUtil = new DAOGenUtil();
private final Class<T> classType = null;
public EditCell(StringConverter<T> converter, Class<T> classType, FXMLControllerSuperType controllerRef) {
this.converter = converter ;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
//*******************************************************************************************************************
//If the user hits ENTER and there are no data entry errors, commit the edit
textField.setOnAction(evt -> {
if ( ! dataEntryError ) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
//*******************************************************************************************************************
//If the cell loses focus and there are no data entry errors, commit the edit
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused && ! dataEntryError ) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
//*******************************************************************************************************************
//Validate data as it's entered
textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
getTableView().removeEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
if ( isDataValid(classType, newValue) ) {
dataEntryError = false;
textField.setStyle("");
DAOGenUtil.clearSystemMessage(controllerRef);
} else {
dataEntryError = true;
textField.setStyle("-fx-background-color: pink;");
displayErrorMessage(classType, controllerRef);
getTableView().addEventFilter(MouseEvent.MOUSE_PRESSED, controllerRef.tvMousePressedHandler);
}
});
//*******************************************************************************************************************
//Trap and process ESCAPE, TAB and SHIFT+TAB
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
TablePosition<S, ?> pos = getTableView().getFocusModel().getFocusedCell();
int maximumVisibleColumnNumber = DAOGenUtil.getMaximumVisibleColumnNumber(getTableView());
if (event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
dataEntryError = false;
DAOGenUtil.clearSystemMessage(controllerRef);
getTableView().setMouseTransparent(false);
} else if ( event.isShiftDown() && event.getCode() == KeyCode.TAB ) {
if ( dataEntryError ) {
event.consume();
} else {
getTableView().setMouseTransparent(false);
getTableView().getSelectionModel().selectLeftCell();
if ( pos.getColumn() == 0 ) {
//We're at the start of a row so position to the end of the previous row
getTableView().getSelectionModel().select(pos.getRow()-1, getTableView().getColumns().get(maximumVisibleColumnNumber));
event.consume();
}
}
} else if ( event.getCode() == KeyCode.TAB ) {
if ( dataEntryError ) {
event.consume();
} else {
getTableView().setMouseTransparent(false);
getTableView().getSelectionModel().selectRightCell();
if ( pos.getColumn() == maximumVisibleColumnNumber ) {
//We're at the end of a row so position to the start of the next row
getTableView().getSelectionModel().select(pos.getRow()+1, getTableView().getColumns().get(0));
event.consume();
}
}
}
});
}
//*******************************************************************************************************************
//Create EditCells for String data types
public static final StringConverter<String> IDENTITY_CONVERTER_STRING = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
public static <S> EditCell<S, String> createStringEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, String>(IDENTITY_CONVERTER_STRING, String.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for Long data types
public static final LongStringConverter IDENTITY_CONVERTER_LONG = new LongStringConverter() {
public String toString(Long object) {
String object2 = DAOGenUtil.formatValue(FORMAT_INTEGER, Long.toString(object));
return ( object == null ? "0" : object2 );
}
public Long fromString(String object) {
Long object3 = Long.parseLong(object.replaceAll(",",""));
return ( object.isEmpty() ? 0 : object3 );
}
};
public static <S> EditCell<S, Long> createLongEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, Long>(IDENTITY_CONVERTER_LONG, Long.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for Double data types
public static final DoubleStringConverter IDENTITY_CONVERTER_DOUBLE = new DoubleStringConverter() {
public String toString(Double object) {
String object2 = DAOGenUtil.formatValue(FORMAT_DOUBLE_FOUR_DECIMALS, Double.toString(object));
return ( object == null ? "0" : object2 );
}
public Double fromString(String object) {
Double object3 = Double.parseDouble(object.replaceAll(",",""));
return ( object.isEmpty() ? 0 : object3 );
}
};
public static <S> EditCell<S, Double> createDoubleEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, Double>(IDENTITY_CONVERTER_DOUBLE, Double.class, controllerRef);
}
//*******************************************************************************************************************
//Create EditCells for LocalDate data types
public static final LocalDateStringConverter IDENTITY_CONVERTER_LOCAL_DATE = new LocalDateStringConverter() {
public String toString(LocalDate object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(FORMAT_DATE_DISPLAY);
String object2 = dateFormatter.format( (TemporalAccessor) object);
return ( object == null ? "0" : object2 );
}
public LocalDate fromString(String object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/M/yyyy");
LocalDate object3 = LocalDate.parse(object, dateFormatter);
return object.isEmpty() ? null : object3;
}
};
public static <S> EditCell<S, LocalDate> createLocalDateEditCell(FXMLControllerSuperType controllerRef) {
return new EditCell<S, LocalDate>(IDENTITY_CONVERTER_LOCAL_DATE, LocalDate.class, controllerRef);
}
//*******************************************************************************************************************
//Code to start, cancel and commit edits
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (! isEditing() && ! item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TablePosition<S,T>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
//*******************************************************************************************************************
//Validate data
public boolean isDataValid(Class<T> classType, String enteredData) {
boolean dataOK = true;
String enteredDataWithoutCommas = "";
if ( classType == Long.class || classType == Double.class ) {
enteredDataWithoutCommas = enteredData.replaceAll(",", "");
}
if ( ( classType == Long.class && ! DAOGenUtil.isIntegerOrLong(enteredDataWithoutCommas) )
|| classType == Double.class && ! DAOGenUtil.isNumeric(enteredDataWithoutCommas)
|| classType == LocalDate.class && ! DAOGenUtil.isDate(enteredData) ) {
dataOK = false;
} else {
dataOK = true;
}
return dataOK;
}
//*******************************************************************************************************************
//Display data entry error messages
public void displayErrorMessage(Class<T> classType, FXMLControllerSuperType controllerRef) {
if ( classType == Long.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid number (expected format 9,999).", COLOUR_DARK_RED);
} else if ( classType == Double.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid number (expected format 9,999.9999).", COLOUR_DARK_RED);
} else if ( classType == LocalDate.class ) {
DAOGenUtil.setSystemMessage(controllerRef, "Invalid date (expected format DAY/MONTH/4-digit YEAR).", COLOUR_DARK_RED);
}
}
}
在 MouseEvent
中,单击的节点可通过 pickResult
访问。请注意,这可能是皮肤引入的控件的子控件,而不是控件本身。您仍然可以通过向上遍历场景层次结构来找到单击的单元格。这使您可以确定点击是否在正在编辑的单元格之外,并根据以下信息决定是否使用该事件:
public static void registerEditingHandler(final TableView<?> tableView) {
EventHandler<MouseEvent> handler = event -> {
TablePosition<?, ?> position = tableView.getEditingCell();
if (position != null) {
Node n = event.getPickResult().getIntersectedNode();
while (n != tableView
&& !(n instanceof TableCell)) {
n = n.getParent();
}
// consume cells outside of cells or on cells not matching the
// editedPosition
if (n == tableView) {
event.consume();
} else {
TableCell<?, ?> cell = (TableCell<?, ?>) n;
if (cell.getIndex() != position.getRow()
|| cell.getTableColumn() != position.getTableColumn()) {
event.consume();
}
}
}
};
tableView.addEventFilter(MouseEvent.MOUSE_CLICKED, handler);
tableView.addEventFilter(MouseEvent.MOUSE_PRESSED, handler);
tableView.addEventFilter(MouseEvent.MOUSE_RELEASED, handler);
}