为什么 DatePicker 在自定义 JavaFX 11 TableCell 中的 startEdit() 之后变得无响应?
Why does DatePicker become unresponsive after a startEdit() in a custom JavaFX 11 TableCell?
我有一个 TableCell
,它在编辑时将日期呈现为 DatePicker
,在不编辑时将日期呈现为纯文本。
当 TableCell
被编辑时,DatePicker
应该获得焦点,它的日期应该被选中并且光标应该位于日期末尾的编辑器中。 DatePicker
的编辑器应该准备好接受用户输入。
但是,目前,当单元格被编辑时,DatePicker
获得焦点但它(或其编辑器?)变得没有响应。未选择日期,并且看不到光标。我无法点击进入编辑器,也无法在编辑器中输入任何内容。唯一有用的是显示日历弹出窗口的按钮。
此外,此 仅在给定行 DatePicker
单元格首次编辑时发生 。下次编辑同一行中的相同单元格时,它工作正常。
如果我在 updateItem()
中将 setContentDisplay()
更改为 GRAPHIC_ONLY 也可以正常工作,但我不想那样做。
下面是演示该问题的 MVCE。
要重现该行为,请双击日期单元格或使用箭头键导航到它并按 F2。 DatePicker
将获得焦点,但不会选择其日期,您将无法单击进入 DatePicker
的编辑器。
现在单击日期单元格外的任意位置,然后返回到同一行中的同一单元格并再次编辑它。然后应该选择日期,您应该可以单击或在编辑器中输入。
我做错了什么?
我用 Google 搜索并在 TableView
中找到了 DatePicker
的示例,但所有设置内容显示为 updateItem()
中的 GRAPHIC_ONLY。除了编辑时,我找不到任何使用 TEXT_ONLY 的东西。
我在 Windows 7 上的 Netbeans 11.0 中使用 11.0.2 版本的 OpenJDK 和 OpenJFX 运行。
package test016;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
public class Test016 extends Application {
private final ObservableList<DataModel> ol = FXCollections.observableArrayList();
private final TableView<DataModel> tv = new TableView();
private Parent createContent() {
loadDummyData();
createTableViewColumns();
tv.getSelectionModel().setCellSelectionEnabled(true);
tv.setEditable(true);
tv.setItems(ol);
tv.getSelectionModel().selectFirst();
BorderPane content = new BorderPane();
content.setCenter(tv);
return content;
}
private void createTableViewColumns() {
TableColumn<DataModel,String> col1 = new TableColumn<>("field1");
TableColumn<DataModel,LocalDate> col2 = new TableColumn<>("field2");
TableColumn<DataModel,String> col3 = new TableColumn<>("field3");
col1.setCellValueFactory(cellData -> cellData.getValue().field1Property());
col1.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col2.setCellValueFactory(cellData -> cellData.getValue().field2Property());
//**************************************************************
//DatePicker TableCell begins here
col2.setCellFactory(dp -> {
DatePicker datePicker = new DatePicker();
StringConverter<LocalDate> dateConverter = new StringConverter<LocalDate>() {
@Override
public String toString(LocalDate object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/MM/yyyy");
return ( object == null ? "" : dateFormatter.format(object) );
}
@Override
public LocalDate fromString(String string) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/MM/yyyy");
return string.isEmpty() ? null : LocalDate.parse(string, dateFormatter);
}
};
datePicker.setConverter(dateConverter);
datePicker.setEditable(true);
datePicker.setDisable(false);
TableCell<DataModel, LocalDate> cell = new TableCell<>() {
@Override
protected void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
//It works I set content display to GRAPHIC_ONLY
setContentDisplay(ContentDisplay.TEXT_ONLY);
//setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setText(dateConverter.toString(item));
datePicker.setValue((LocalDate) item);
setGraphic(datePicker);
}
}
@Override
public void startEdit() {
super.startEdit();
if ( getGraphic() != null && getGraphic() instanceof DatePicker ) {
//The problem only occurs when I set content display to TEXT_ONLY in updateItem().
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
//If I requestFocus() on the graphic, the DatePicker gets the focus but I cannot edit the date
//by clicking in the editor and typing something in.
//If I do NOT requestFocus() on the graphic, the cell containing the DatePicker gets focus but
//I can then click in the DatePicker's editor and type something in. However, I cannot just
//start typing as the DatePicker doesn't have focus.
//This happens irrespective of whether I double-click on a cell to start the edit or
//navigate to the cell via the keyboard and then hit F2.
//The behaviour only appears the first time I edit a DatePicker in a given row.
//The second and subsequent edits for the same row work fine.
getGraphic().requestFocus();
//Requesting focus on the DatePicker's editor doesn't appear to have any effect, either with or
//without first requesting focus on the graphic.
( (DatePicker) getGraphic() ).getEditor().requestFocus();
( (DatePicker) getGraphic() ).getEditor().selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
};
return cell;
});
//**************************************************************
col2.setPrefWidth(120);
col3.setCellValueFactory(cellData -> cellData.getValue().field3Property());
col3.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
tv.getColumns().addAll(Arrays.asList(col1, col2, col3));
}
private void loadDummyData() {
ol.add(new DataModel("1", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("2", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("3", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("4", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("5", LocalDate.parse("2001-01-01"), "x"));
}
private class DataModel {
private final StringProperty field1;
private final ObjectProperty<LocalDate> field2;
private final StringProperty field3;
public DataModel(
String field1,
LocalDate field2,
String field3
) {
this.field1 = new SimpleStringProperty(field1);
this.field2 = new SimpleObjectProperty<>(field2);
this.field3 = new SimpleStringProperty(field3);
}
public String getField1() {return field1.get().trim();}
public void setField1(String field1) {this.field1.set(field1);}
public StringProperty field1Property() {return field1;}
public LocalDate getField2() {return field2.get();}
public void setField2(LocalDate field2) {this.field2.set(field2);}
public ObjectProperty<LocalDate> field2Property() {return field2;}
public String getField3() {return field3.get().trim();}
public void setField3(String field3) {this.field3.set(field3);}
public StringProperty field3Property() {return field3;}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle("OpenJFX11 - DatePicker in TableView");
stage.setWidth(600D);
stage.setHeight(600D);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
感谢 Slaw 的建议,无响应问题的解决方法是在 DatePicker
上调用 applyCss()
它被设置为图形但 之前 requestFocus()
和 selectAll()
.
虽然 selectAll()
仍然只会在对同一行中的同一单元格进行第二次编辑时生效,但 DatePicker
现在至少会响应编辑。
@Override
public void startEdit() {
super.startEdit();
if ( getGraphic() != null && getGraphic() instanceof DatePicker ) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
( (DatePicker) getGraphic() ).applyCss(); //Workaround for the unresponsiveness issue.
getGraphic().requestFocus();
( (DatePicker) getGraphic() ).getEditor().requestFocus();
( (DatePicker) getGraphic() ).getEditor().selectAll();
}
}
我有一个 TableCell
,它在编辑时将日期呈现为 DatePicker
,在不编辑时将日期呈现为纯文本。
当 TableCell
被编辑时,DatePicker
应该获得焦点,它的日期应该被选中并且光标应该位于日期末尾的编辑器中。 DatePicker
的编辑器应该准备好接受用户输入。
但是,目前,当单元格被编辑时,DatePicker
获得焦点但它(或其编辑器?)变得没有响应。未选择日期,并且看不到光标。我无法点击进入编辑器,也无法在编辑器中输入任何内容。唯一有用的是显示日历弹出窗口的按钮。
此外,此 仅在给定行 DatePicker
单元格首次编辑时发生 。下次编辑同一行中的相同单元格时,它工作正常。
如果我在 updateItem()
中将 setContentDisplay()
更改为 GRAPHIC_ONLY 也可以正常工作,但我不想那样做。
下面是演示该问题的 MVCE。
要重现该行为,请双击日期单元格或使用箭头键导航到它并按 F2。 DatePicker
将获得焦点,但不会选择其日期,您将无法单击进入 DatePicker
的编辑器。
现在单击日期单元格外的任意位置,然后返回到同一行中的同一单元格并再次编辑它。然后应该选择日期,您应该可以单击或在编辑器中输入。
我做错了什么?
我用 Google 搜索并在 TableView
中找到了 DatePicker
的示例,但所有设置内容显示为 updateItem()
中的 GRAPHIC_ONLY。除了编辑时,我找不到任何使用 TEXT_ONLY 的东西。
我在 Windows 7 上的 Netbeans 11.0 中使用 11.0.2 版本的 OpenJDK 和 OpenJFX 运行。
package test016;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
public class Test016 extends Application {
private final ObservableList<DataModel> ol = FXCollections.observableArrayList();
private final TableView<DataModel> tv = new TableView();
private Parent createContent() {
loadDummyData();
createTableViewColumns();
tv.getSelectionModel().setCellSelectionEnabled(true);
tv.setEditable(true);
tv.setItems(ol);
tv.getSelectionModel().selectFirst();
BorderPane content = new BorderPane();
content.setCenter(tv);
return content;
}
private void createTableViewColumns() {
TableColumn<DataModel,String> col1 = new TableColumn<>("field1");
TableColumn<DataModel,LocalDate> col2 = new TableColumn<>("field2");
TableColumn<DataModel,String> col3 = new TableColumn<>("field3");
col1.setCellValueFactory(cellData -> cellData.getValue().field1Property());
col1.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col2.setCellValueFactory(cellData -> cellData.getValue().field2Property());
//**************************************************************
//DatePicker TableCell begins here
col2.setCellFactory(dp -> {
DatePicker datePicker = new DatePicker();
StringConverter<LocalDate> dateConverter = new StringConverter<LocalDate>() {
@Override
public String toString(LocalDate object) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/MM/yyyy");
return ( object == null ? "" : dateFormatter.format(object) );
}
@Override
public LocalDate fromString(String string) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/MM/yyyy");
return string.isEmpty() ? null : LocalDate.parse(string, dateFormatter);
}
};
datePicker.setConverter(dateConverter);
datePicker.setEditable(true);
datePicker.setDisable(false);
TableCell<DataModel, LocalDate> cell = new TableCell<>() {
@Override
protected void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
//It works I set content display to GRAPHIC_ONLY
setContentDisplay(ContentDisplay.TEXT_ONLY);
//setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setText(dateConverter.toString(item));
datePicker.setValue((LocalDate) item);
setGraphic(datePicker);
}
}
@Override
public void startEdit() {
super.startEdit();
if ( getGraphic() != null && getGraphic() instanceof DatePicker ) {
//The problem only occurs when I set content display to TEXT_ONLY in updateItem().
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
//If I requestFocus() on the graphic, the DatePicker gets the focus but I cannot edit the date
//by clicking in the editor and typing something in.
//If I do NOT requestFocus() on the graphic, the cell containing the DatePicker gets focus but
//I can then click in the DatePicker's editor and type something in. However, I cannot just
//start typing as the DatePicker doesn't have focus.
//This happens irrespective of whether I double-click on a cell to start the edit or
//navigate to the cell via the keyboard and then hit F2.
//The behaviour only appears the first time I edit a DatePicker in a given row.
//The second and subsequent edits for the same row work fine.
getGraphic().requestFocus();
//Requesting focus on the DatePicker's editor doesn't appear to have any effect, either with or
//without first requesting focus on the graphic.
( (DatePicker) getGraphic() ).getEditor().requestFocus();
( (DatePicker) getGraphic() ).getEditor().selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
};
return cell;
});
//**************************************************************
col2.setPrefWidth(120);
col3.setCellValueFactory(cellData -> cellData.getValue().field3Property());
col3.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
tv.getColumns().addAll(Arrays.asList(col1, col2, col3));
}
private void loadDummyData() {
ol.add(new DataModel("1", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("2", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("3", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("4", LocalDate.parse("2001-01-01"), "x"));
ol.add(new DataModel("5", LocalDate.parse("2001-01-01"), "x"));
}
private class DataModel {
private final StringProperty field1;
private final ObjectProperty<LocalDate> field2;
private final StringProperty field3;
public DataModel(
String field1,
LocalDate field2,
String field3
) {
this.field1 = new SimpleStringProperty(field1);
this.field2 = new SimpleObjectProperty<>(field2);
this.field3 = new SimpleStringProperty(field3);
}
public String getField1() {return field1.get().trim();}
public void setField1(String field1) {this.field1.set(field1);}
public StringProperty field1Property() {return field1;}
public LocalDate getField2() {return field2.get();}
public void setField2(LocalDate field2) {this.field2.set(field2);}
public ObjectProperty<LocalDate> field2Property() {return field2;}
public String getField3() {return field3.get().trim();}
public void setField3(String field3) {this.field3.set(field3);}
public StringProperty field3Property() {return field3;}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle("OpenJFX11 - DatePicker in TableView");
stage.setWidth(600D);
stage.setHeight(600D);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
感谢 Slaw 的建议,无响应问题的解决方法是在 DatePicker
上调用 applyCss()
它被设置为图形但 之前 requestFocus()
和 selectAll()
.
虽然 selectAll()
仍然只会在对同一行中的同一单元格进行第二次编辑时生效,但 DatePicker
现在至少会响应编辑。
@Override
public void startEdit() {
super.startEdit();
if ( getGraphic() != null && getGraphic() instanceof DatePicker ) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
( (DatePicker) getGraphic() ).applyCss(); //Workaround for the unresponsiveness issue.
getGraphic().requestFocus();
( (DatePicker) getGraphic() ).getEditor().requestFocus();
( (DatePicker) getGraphic() ).getEditor().selectAll();
}
}