在 JavaFX ListView 中选择自定义列表单元格的内部控件

问题描述 投票:0回答:1

我有:

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 被重用..如何将焦点放在所选项目上?谢谢。

listview javafx
1个回答
0
投票

首先提供问题的完整背景将帮助其他人了解您遇到的确切问题。

从你的代码中我不确定一些事情。您在哪里实施部分有效的解决方案?在 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();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.