in Education by
I'd like to use the arrow/enter keys to traverse the cells in TableView, however, if I try to implement it in my custom EditCell class, it doesn't seem to work. Is there a way to make this happen? I've tried a listener on the TextField but it doesn't actually start the focus in the actual cell. Here is my code: Tester.java package tester; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.layout.HBox; import javafx.stage.Stage; import javafx.util.Callback; public class Tester extends Application { @Override public void start(Stage primaryStage) { TableView table = new TableView<>(); Callback, TableCell> textFactoryEditable = (TableColumn p) -> new EditableTextCell(); TableColumn column1 = new TableColumn<>("Test1"); column1.setCellValueFactory(cellData -> cellData.getValue().getString1Property()); column1.setEditable(true); column1.setCellFactory(textFactoryEditable); table.getColumns().add(column1); TableColumn column2 = new TableColumn<>("Test2"); column2.setCellValueFactory(cellData -> cellData.getValue().getString2Property()); column2.setEditable(true); column2.setCellFactory(textFactoryEditable); table.getColumns().add(column2); table.getItems().add(new LineItem()); table.getItems().add(new LineItem()); table.getItems().add(new LineItem()); table.setPrefWidth(500); HBox root = new HBox(); root.getChildren().addAll(table); Scene scene = new Scene(root, 500, 500); primaryStage.setTitle("Hello World!"); primaryStage.setScene(scene); primaryStage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } } LineItem.java package tester; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class LineItem { private final StringProperty string1; private final StringProperty string2; public LineItem() { this.string1 = new SimpleStringProperty(); this.string2 = new SimpleStringProperty(); } public final StringProperty getString1Property() { return this.string1; } public final StringProperty getString2Property() { return this.string2; } } EditableTextCell.java package tester; import java.util.Objects; import javafx.beans.value.ObservableValue; import javafx.beans.value.WritableValue; import javafx.geometry.Pos; import javafx.scene.control.TableCell; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; public class EditableTextCell extends TableCell { private final TextField textField; private boolean updating = false; public EditableTextCell() { textField = new TextField(); textField.setAlignment(Pos.CENTER_RIGHT); textField.textProperty().addListener((ObservableValue<? extends String> o, String oldValue, String newValue) -> { if (!updating) { ((WritableValue) getTableColumn().getCellObservableValue((E) getTableRow().getItem())).setValue(newValue); getTableView().scrollTo(getTableRow().getIndex()); getTableView().scrollToColumn(getTableColumn()); } }); textField.setOnKeyPressed((KeyEvent ke) -> { switch (ke.getCode()) { case DOWN: getTableView().getFocusModel().focusBelowCell(); break; case UP: getTableView().getFocusModel().focusAboveCell(); break; case RIGHT: getTableView().getFocusModel().focusRightCell(); break; case LEFT: getTableView().getFocusModel().focusLeftCell(); break; default: break; } }); } @Override protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (empty) { setGraphic(null); } else { setGraphic(textField); if (!Objects.equals(textField.getText(), item)) { // prevent own updates from moving the cursor updating = true; textField.setText(item); updating = false; } } } } JavaScript questions and answers, JavaScript questions pdf, JavaScript question bank, JavaScript questions and answers pdf, mcq on JavaScript pdf, JavaScript questions and solutions, JavaScript mcq Test , Interview JavaScript questions, JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)

1 Answer

0 votes
by
Row Selection Mode Despite my comment, it doesn't look like you need to enable cell selection for this. Taking inspiration from the implementation of CheckBoxTableCell, your custom TableCell should take some form of callback to get the model property; it can also require a StringConverter, allowing you to use the TableCell with more than just Strings. Here's an example: import java.util.Objects; import java.util.function.IntFunction; import javafx.beans.binding.Bindings; import javafx.beans.property.ObjectProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView.TableViewFocusModel; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.util.Callback; import javafx.util.StringConverter; import javafx.util.converter.DefaultStringConverter; public class CustomTableCell extends TableCell { public static Callback, TableCell> forTableColumn( IntFunction> extractor) { return forTableColumn(extractor, new DefaultStringConverter()); } public static Callback, TableCell> forTableColumn( IntFunction> extractor, StringConverter converter) { Objects.requireNonNull(extractor); Objects.requireNonNull(converter); return column -> new CustomTableCell<>(extractor, converter); } private final ObjectProperty>> extractor = new SimpleObjectProperty<>(this, "extractor"); public final void setExtractor(IntFunction> callback) { extractor.set(callback); } public final IntFunction> getExtractor() { return extractor.get(); } public final ObjectProperty>> extractorProperty() { return extractor; } private final ObjectProperty> converter = new SimpleObjectProperty<>(this, "converter"); public final void setConverter(StringConverter converter) { this.converter.set(converter); } public final StringConverter getConverter() { return converter.get(); } public final ObjectProperty> converterProperty() { return converter; } private Property property; private TextField textField; public CustomTableCell(IntFunction> extractor, StringConverter converter) { setExtractor(extractor); setConverter(converter); // Assumes this TableCell will never become part of a different TableView // after the first one. Also assumes the focus model of the TableView will // never change. These are not great assumptions (especially the latter), // but this is only an example. tableViewProperty().addListener((obs, oldTable, newTable) -> newTable.getFocusModel().focusedCellProperty().addListener((obs2, oldPos, newPos) -> { if (getIndex() == newPos.getRow() && getTableColumn() == newPos.getTableColumn()) { textField.requestFocus(); } }) ); } @Override protected void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); cleanUpProperty(); } else { initializeTextField(); cleanUpProperty(); property = getExtractor().apply(getIndex()); Bindings.bindBidirectional(textField.textProperty(), property, getConverter()); setGraphic(textField); if (getTableView().getFocusModel().isFocused(getIndex(), getTableColumn())) { textField.requestFocus(); } } } private void cleanUpProperty() { if (property != null) { Bindings.unbindBidirectional(textField.textProperty(), property); property = null; } } private void initializeTextField() { if (textField == null) { textField = new TextField(); textField.addEventFilter(KeyEvent.KEY_PRESSED, this::processArrowKeys); textField.focusedProperty().addListener((observable, wasFocused, isFocused) -> { if (isFocused) { getTableView().getFocusModel().focus(getIndex(), getTableColumn()); } }); } } private void processArrowKeys(KeyEvent event) { if (event.getCode().isArrowKey()) { event.consume(); TableViewFocusModel model = getTableView().getFocusModel(); switch (event.getCode()) { case UP: model.focusAboveCell(); break; case RIGHT: model.focusRightCell(); break; case DOWN: model.focusBelowCell(); break; case LEFT: model.focusLeftCell(); break; default: throw new AssertionError(event.getCode().name()); } getTableView().scrollTo(model.getFocusedCell().getRow()); getTableView().scrollToColumnIndex(model.getFocusedCell().getColumn()); } } } The example is not exhaustive and makes assumptions that are not guaranteed, but it's only an example and so I leave any tweaks up to you. One such improvement might be to include a TextFormatter somehow. That said, I believe it provides the basic functionality you're looking for. To use this cell, you would only set the cellFactory of each TableColumn. It is not necessary to set the cellValueFactory and doing so might actually be detrimental, depending on how updateItem gets called. Basically, it'd look something like: TableView table = ...; TableColumn column = new TableColumn<>("Column"); column.setCellFactory(CustomTableCell.forTableColumn(i -> table.getItems().get(i).someProperty())); table.getColumns().add(column); Cell Selection Mode This behavior you're attempting to implement seems inherently cell based, however, and as such it's probably better to enable cell selection. This allows the custom TableCell to base it's behavior on selection, rather than focus, and leaves the arrow key handling to the TableView. Here's a slightly modified version of the above example: import java.util.Objects; import java.util.function.IntFunction; import javafx.beans.binding.Bindings; import javafx.beans.property.ObjectProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleObjectProperty; import javafx.event.EventDispatcher; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.util.Callback; import javafx.util.StringConverter; import javafx.util.converter.DefaultStringConverter; public class CustomTableCell extends TableCell { /* * -- CODE OMITTED -- * * The factory methods (forTableColumn) and properties (extractor * and converter) have been omitted for brevity. They are defined * and used exactly the same way as in the previous example. */ private Property property; private TextField textField; public CustomTableCell(IntFunction> extractor, StringConverter converter) { setExtractor(extractor); setConverter(converter); } @Override public void updateSelected(boolean selected) { super.updateSelected(selected); if (selected && !isEmpty()) { textField.requestFocus(); } } @Override protected void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); clearProperty(); } else { initializeTextField(); clearProperty(); property = getExtractor().apply(getIndex()); Bindings.bindBidirectional(textField.textProperty(), property, getConverter()); setGraphic(textField); if (isSelected()) { textField.requestFocus(); } } } private void clearProperty() { if (property != null) { Bindings.unbindBidirectional(textField.textProperty(), property); textField.setText(null); property = null; } } private void initializeTextField() { if (textField == null) { textField = new TextField(); textField.focusedProperty().addListener((observable, wasFocused, isFocused) -> { if (isFocused && !isSelected()) { getTableView().getSelectionModel().clearAndSelect(getIndex(), getTableColumn()); } }); /* * TableView has key handlers that will select cells based on arrow keys being * pressed, scrolling to them if necessary. I find this mechanism looks cleaner * because, unlike TableView#scrollTo, it doesn't cause the cell to jump to the * top of the TableView. * * The way this works is by bypassing the TextField if, and only if, the event * is a KEY_PRESSED event and the pressed key is an arrow key. This lets the * event bubble up back to the TableView and let it do what it needs to. All * other key events are given to the TextField for normal processing. * * NOTE: The behavior being relied upon here is added by the default TableViewSkin * and its corresponding TableViewBehavior. This may not work if a custom * TableViewSkin skin is used. */ EventDispatcher oldDispatcher = textField.getEventDispatcher(); textField.setEventDispatcher((event, tail) -> { if (event.getEventType() == KeyEvent.KEY_PRESSED && ((KeyEvent) event).getCode().isArrowKey()) { return event; } else { return oldDispatcher.dispatchEvent(event, tail); } }); } } } Notes Neither approach works well when using SelectionMode.MULTIPLE (and actually selecting multiple rows/cells). The ObservableList set on the TableView cannot have an extractor defined. For some reason this causes the table to select the next right cell when you type into the TextField. Only tested both approaches with JavaFX 12.

Related questions

0 votes
    If the size of the array used to implement a circular queue is MAX_SIZE. How rear moves to traverse inorder ... Framework of Java Select the correct answer from above options...
asked Mar 1, 2022 in Education by JackTerrance
0 votes
    By using Edit mode,how will you make modification in cell contents Select the correct answer from above options...
asked Nov 28, 2021 in Education by JackTerrance
0 votes
    Which mode allows us to run program interactively while watching source code and variables during execution? (a) ... Servlet of Java Select the correct answer from above options...
asked Feb 22, 2022 in Education by JackTerrance
0 votes
    to edit the interactive mode which key combination should be press Select the correct answer from above options...
asked Dec 21, 2021 in Education by JackTerrance
0 votes
    If the size of the array used to implement a circular queue is MAX_SIZE. How rear moves to traverse ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Oct 25, 2021 in Education by JackTerrance
0 votes
    I think I shall reframe my question from Where should you use BlockingQueue Implementations instead of Simple Queue ... for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 5, 2022 in Education by JackTerrance
0 votes
    What are the use of front and rear pointers in CircularQueue implementation? (a) Front pointer points to first ... Framework of Java Select the correct answer from above options...
asked Mar 1, 2022 in Education by JackTerrance
0 votes
    What is the use of try & catch? (a) It allows us to manually handle the exception (b) It allows ... section Exception Handling of Java Select the correct answer from above options...
asked Mar 1, 2022 in Education by JackTerrance
0 votes
    What is the use of Observable class? (a) It is used to create global subclasses (b) It is used to ... More Utility Classes of Java Select the correct answer from above options...
asked Feb 23, 2022 in Education by JackTerrance
0 votes
    Which of these keywords is used by a class to use an interface defined previously? (a) import (b) Import (c ... & Packages of Java Select the correct answer from above options...
asked Feb 23, 2022 in Education by JackTerrance
0 votes
    Which of the following is an incorrect statement regarding the use of generics and parameterized types in Java? (a ... Packages of Java Select the correct answer from above options...
asked Feb 23, 2022 in Education by JackTerrance
0 votes
    What is use of wildcards? (a) It is used in cases when type being operated upon is not known (b) It is ... in portion Generics of Java Select the correct answer from above options...
asked Feb 22, 2022 in Education by JackTerrance
0 votes
    What is the use of Flushable interface? (a) Flushes this stream by writing any buffered output to the underlying ... & API of Java Select the correct answer from above options...
asked Feb 16, 2022 in Education by JackTerrance
0 votes
    In Ruby, Dir.glob("**/*.rb") (for instance) doesn't traverse symlinked directories. Is it ... JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 11, 2022 in Education by JackTerrance
0 votes
    A JavaScript program can traverse and manipulate document content through __________ (a) Element Object (b) Document ... for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Oct 22, 2021 in Education by JackTerrance
...