我有:
public class TaskCell extends ListCell<Activity>...
..
private TextField textField = new TextField();
private HBox content = new HBox(..textField..);
@Override
public void updateItem(final Activity activity, boolean empty) {
super.updateItem(activity, empty);
...
textField.setText(activity.getTitle());
setGraphic(content);
}
Content 是一个带有 TextField 和其他控件的 HBox。
通过单击或通过以下方式选择列表项:
listView.getSelectionModel().select(1);
起作用并选择整个项目。现在我想使用 Key.Tab 选择内部 TextField 进行编辑。这怎么可能?问题不是关于 KeyEvents,而是如何在所选 ListItem 上的 TextField(任何控件)上获取 requestFocus。
最好且仅部分有效的解决方案:
TaskCell selectedTaskCell = (TaskCell) listView.lookup(".cell:selected");
HBox hBox = (HBox) selectedTaskCell.getGraphic();
TextField textField = (TextField) hBox.getChildren().get(1);
textField.requestFocus();
textField.setVisible(false);
这确实通过调试验证了正确选择的单元格。但是 requestFocus/setVisible 总是在第一个列表项上完成。从逻辑上讲,随着 ListCell 被重用..如何将焦点放在所选项目上?谢谢。
首先提供问题的完整背景将帮助其他人了解您遇到的确切问题。
从你的代码中我不确定一些事情。您在哪里实施部分有效的解决方案?在 KeyEvent 处理程序/过滤器中?哪种 KeyEvent 类型?因为如果你不使用正确的方法,你的结果就会改变。其次,我真的不确定为什么在请求焦点后关闭 TextField 的可见性。
无论如何,从您提供的代码来看,我相信您希望在按下 Tab 键时将焦点集中在选定的单元格 TextField 上。
首先,我注意到,“.cell:selected”的查找调用总是返回第一个单元格。事实上,如果您查找“.cell:fake”,也将仅返回第一个单元格,而不管伪类是什么;)。因此,为了解决这个问题,我有一个想法,为选定的单元格设置一个 id,我们可以通过 id 来查找它。
在单元格的构造函数中:
public TaskCell() {
selectedProperty().addListener((obs, old, val) -> setId(val ? "mycell" : null));
}
其次,默认的 Tab 键行为是在 ListView 上的
KEY_PRESSED
的冒泡阶段(处理程序)中实现的。因此,您需要确保在请求焦点位于 TextField 后消费该事件。否则,事件将进一步传播并实现默认的 Tab 行为并将焦点放在第一个文本字段上。
listView.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.TAB) {
Node tf = listView.lookup("#mycell .text-field");
if (tf != null) {
tf.requestFocus();
e.consume(); // This is a required.
}
}
});
注意:如果您不想消费该事件,可以在
KEY_RELEASED
处理程序中进行。但是您会看到焦点转移的快速跳跃:首先在第一个单元格文本字段上,然后在选定的单元格文本字段上。 (看起来会很奇怪)
因此结合以上两个更改,下面是完整的工作演示:
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class ListCellTextFieldDemo extends Application {
@Override
public void start(final Stage stage) throws Exception {
ObservableList<Activity> activities = FXCollections.observableArrayList();
IntStream.range(1, 30).forEach(i -> activities.add(new Activity(i, "Title " + i)));
ListView<Activity> listView = new ListView<>();
listView.setItems(activities);
listView.setCellFactory(activityListView -> new TaskCell());
listView.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.TAB) {
Node tf = listView.lookup("#mycell .text-field");
if (tf != null) {
tf.requestFocus();
e.consume(); // This is a required.
}
}
});
VBox root = new VBox(listView);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 300, 450);
stage.setScene(scene);
stage.setTitle("ListCell Demo");
stage.show();
}
class TaskCell extends ListCell<Activity> {
private Label tokenLbl = new Label();
private TextField field = new TextField();
private HBox content = new HBox(15, tokenLbl, field);
public TaskCell() {
selectedProperty().addListener((obs, old, val) -> setId(val ? "mycell" : null));
}
@Override
protected void updateItem(final Activity activity, final boolean b) {
super.updateItem(activity, b);
if (activity != null) {
tokenLbl.setText(activity.getToken() + "");
field.setText(activity.getTitle());
setGraphic(content);
} else {
tokenLbl.setText("");
field.setText("");
setGraphic(null);
}
}
}
class Activity {
private StringProperty title = new SimpleStringProperty();
private IntegerProperty token = new SimpleIntegerProperty();
public Activity(int t, String n) {
title.set(n);
token.set(t);
}
public String getTitle() {
return title.get();
}
public int getToken() {
return token.get();
}
}
}