2 回答

TA貢獻1815條經驗 獲得超10個贊
行選擇模式
盡管我的評論,但看起來您并不需要為此啟用單元格選擇。從CheckBoxTableCell
的實現中汲取靈感,您的自定義應該采用某種形式的回調來獲取模型屬性;它還可能需要一個 StringConverter
,允許您使用不僅僅包含 s 的 。下面是一個示例:TableCell
TableCell
String
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<S, T> extends TableCell<S, T> {
public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn(
IntFunction<Property<String>> extractor) {
return forTableColumn(extractor, new DefaultStringConverter());
}
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(
IntFunction<Property<T>> extractor, StringConverter<T> converter) {
Objects.requireNonNull(extractor);
Objects.requireNonNull(converter);
return column -> new CustomTableCell<>(extractor, converter);
}
private final ObjectProperty<IntFunction<Property<T>>> extractor = new SimpleObjectProperty<>(this, "extractor");
public final void setExtractor(IntFunction<Property<T>> callback) { extractor.set(callback); }
public final IntFunction<Property<T>> getExtractor() { return extractor.get(); }
public final ObjectProperty<IntFunction<Property<T>>> extractorProperty() { return extractor; }
private final ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this, "converter");
public final void setConverter(StringConverter<T> converter) { this.converter.set(converter); }
public final StringConverter<T> getConverter() { return converter.get(); }
public final ObjectProperty<StringConverter<T>> converterProperty() { return converter; }
private Property<T> property;
private TextField textField;
public CustomTableCell(IntFunction<Property<T>> extractor, StringConverter<T> 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<S> 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());
}
}
}
這個例子并不詳盡,并且做出了不能保證的假設,但它只是一個例子,所以我把任何調整留給你。其中一個改進可能是以某種方式包含文本格式化程序。也就是說,我相信它提供了您正在尋找的基本功能。
要使用此單元格,您只需將 每個 的 設置。沒有必要設置,這樣做實際上可能是有害的,這取決于如何被調用?;旧?,它看起來像這樣:cellFactoryTableColumncellValueFactoryupdateItem
TableView<YourModel> table = ...;
TableColumn<YourModel, String> column = new TableColumn<>("Column");
column.setCellFactory(CustomTableCell.forTableColumn(i -> table.getItems().get(i).someProperty()));
table.getColumns().add(column);
單元格選擇模式
但是,您嘗試實現的此行為本身似乎基于單元格,因此啟用單元格選擇可能更好。這允許自定義人員將其行為基于選擇而不是焦點,并將箭頭鍵處理留給 .下面是上述示例的略微修改版本:TableCellTableView
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<S, T> extends TableCell<S, T> {
/*
* -- 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<T> property;
private TextField textField;
public CustomTableCell(IntFunction<Property<T>> extractor, StringConverter<T> 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);
}
});
}
}
}
筆記
使用(并且實際選擇多個行/單元格)時,這兩種方法都不起作用。
SelectionMode.MULTIPLE
上的集不能定義提取器。由于某種原因,這會導致表在您鍵入 時選擇下一個右側單元格。
ObservableList
TableView
TextField
僅使用 JavaFX 12 測試了這兩種方法。

TA貢獻1776條經驗 獲得超12個贊
多虧了Slaw,才想通了。
首先啟用單元格選擇,table.getSelectionModel().setCellSelectionEnabled(true);
然后在 EditableTextCell.java類中:
this.focusedProperty().addListener((ObservableValue<? extends Boolean> o, Boolean oldValue, Boolean newValue) ->
{
if (newValue)
{
textField.requestFocus();
}
});
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> o, Boolean oldValue, Boolean newValue) ->
{
if (newValue)
{
getTableView().getFocusModel().focus(getTableRow().getIndex(), getTableColumn());
}
}
textField.setOnKeyPressed((KeyEvent ke) ->
{
switch (ke.getCode())
{
case DOWN:
getTableView().getFocusModel().focusBelowCell();
ke.consume();
break;
case ENTER:
getTableView().getFocusModel().focusBelowCell();
ke.consume();
break;
case UP:
getTableView().getFocusModel().focusAboveCell();
ke.consume();
break;
case RIGHT:
getTableView().getFocusModel().focusRightCell();
ke.consume();
break;
case LEFT:
getTableView().getFocusModel().focusLeftCell();
ke.consume();
break;
default:
break;
}
});
添加回答
舉報