当前位置: 首页 > 知识库问答 >
问题:

JavaFX ListView编辑TextFieldListCell

淳于亦
2023-03-14

我正在研究一个客户机-服务器解决方案,我有一个与我连接的客户机的ListView。用户应该能够通过编辑ListView中的名称来远程重命名客户端。我已经阅读了很多关于编辑ListView单元格的内容,但是我还没有找到一个很好的例子,我可以只更改类的成员属性。大多数示例都带有字符串列表,在我看来,如果ListView中的项不仅仅是字符串,这就不是现代软件开发了。

我要做的是更改客户端内部的属性name

class Client {
    private String name;
    
    public String getName(){
        return name;
    }
    
    public String setName(String val){
        name = val;
    }
}
this.listViewClients.setCellFactory(TextFieldListCell
 .forListView(new NetworkClientStringConverter(this.clientController)));
    null

共有1个答案

桓嘉谊
2023-03-14

使用标准的TextFieldListCell,这有点棘手,但是下面的操作似乎是可行的。

假设在整个问题中有一个客户机模型类:

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Client {

    private final StringProperty name = new SimpleStringProperty();

    public final StringProperty nameProperty() {
        return this.name;
    }


    public final String getName() {
        return this.nameProperty().get();
    }


    public final void setName(final String name) {
        this.nameProperty().set(name);
    }

}

(fwiw我认为,如果您使用标准Java Bean属性而不是JavaFX属性,那么这里的所有内容都仍然适用)。

public class ClientConverter extends StringConverter<Client> {
    private final ListCell<Client> cell;
    public ClientConverter(ListCell<Client> cell) {
        this.cell = cell ;
    }
    @Override
    public String toString(Client client) {
        return client.getName();
    }

    @Override
    public Client fromString(String string) {
        Client client = cell.getItem();
        client.setName(string);
        return client ;
    }

}
listViewClients.setCellFactory(lv -> {
    TextFieldListCell<Client> cell = new TextFieldListCell<>();
    cell.setConverter(new ClientConverter(cell));
    return cell ;
});
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class EditableListView extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<Client> listViewClients = new ListView<>();
        for (int i= 1 ; i <= 20; i++) {
            Client c = new Client();
            c.setName("Client "+i);
            listViewClients.getItems().add(c);
        }

        listViewClients.setEditable(true);

        listViewClients.setCellFactory(lv -> {
            TextFieldListCell<Client> cell = new TextFieldListCell<>();
            cell.setConverter(new ClientConverter(cell));
            return cell ;
        });

        // debug:

        Button debug = new Button("Show clients");
        debug.setOnAction(e -> listViewClients.getItems().stream().map(Client::getName).forEach(System.out::println));

        BorderPane root = new BorderPane(listViewClients, null, null, debug, null);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }



    public static class ClientConverter extends StringConverter<Client> {
        private final ListCell<Client> cell;
        public ClientConverter(ListCell<Client> cell) {
            this.cell = cell ;
        }
        @Override
        public String toString(Client client) {
            return client.getName();
        }

        @Override
        public Client fromString(String string) {
            Client client = cell.getItem();
            client.setName(string);
            return client ;
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}

不过,这感觉有点麻烦,因为更新模型(Client)类中的数据实际上不是StringConverter的工作。我可能更喜欢在这里从头创建cell实现。

代码多了一点,但这感觉更安全:

public class ClientListCell extends ListCell<Client> {
    private final TextField textField = new TextField();

    public ClientListCell() {
        textField.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
            if (e.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
            }
        });
        textField.setOnAction(e -> {
            getItem().setName(textField.getText());
            setText(textField.getText());
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        });
        setGraphic(textField);
    }

    @Override
    protected void updateItem(Client client, boolean empty) {
        super.updateItem(client, empty);
        if (isEditing()) {
            textField.setText(client.getName());
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        } else {
            setContentDisplay(ContentDisplay.TEXT_ONLY);
            if (empty) {
                setText(null);
            } else {
                setText(client.getName());
            }
        }
    }

    @Override
    public void startEdit() {
        super.startEdit();
        textField.setText(getItem().getName());
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        textField.requestFocus();
        textField.selectAll();
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(getItem().getName());
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }
}

使用此单元实现的SSCE是

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class EditableListView extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<Client> listViewClients = new ListView<>();
        for (int i= 1 ; i <= 20; i++) {
            Client c = new Client();
            c.setName("Client "+i);
            listViewClients.getItems().add(c);
        }

        listViewClients.setEditable(true);

        listViewClients.setCellFactory(lv -> new ClientListCell());

        // debug:

        Button debug = new Button("Show clients");
        debug.setOnAction(e -> listViewClients.getItems().stream().map(Client::getName).forEach(System.out::println));

        BorderPane root = new BorderPane(listViewClients, null, null, debug, null);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
 类似资料:
  • 在JavaFX 8中,我尝试在将新行添加到表中后编辑单元格,以优化用户体验。 选择了正确的行,但单元不会进入编辑模式。嗯,我很偶然地看到了这种情况,但很难再现。我做错了什么?

  •   复制 剪切 字符 y x 行 yy dd 需先在可视模式中选取 p 在光标后粘贴 P 在光标前粘贴 u 撤消 Ctrl+r 重做 Ctrl+y 逐字克隆上一行内容 Ctrl+e 逐字克隆下一行内容

  • 问题内容: 使用什么是能够编辑内容的最佳方法? 在我理想的情况下, 添加的 生日将是一个超链接,点击该链接将显示一个编辑表单-与带有更新按钮的当前添加表单相同。 实时预览(插播) HTML: App.js: 问题答案: 您应该将表单放在每个节点内,分别使用和启用和禁用编辑。像这样: 这里的关键点是: 我已将控件更改为本地范围 已添加到,因此我们可以在编辑时显示它 添加了带有的,以便在编辑时隐藏内容

  • Vim提供了许多命令,使编辑功能非常强大。 在本章中,将讨论以下主题内容 - 插入 附加 打开新行 替换 更改 更换 加入 1. 在光标前插入文本 要在光标之前插入文本执行以下步骤 - 打开Vim 切换到插入模式 在行的开头插入文本 假设位于行的中间,并且希望在当前行的开头插入文本,然后执行以下步骤 - 切换到命令模式 激活插入模式 此操作将光标移动到当前行的开头并在插入模式下切换Vim。 在光标

  • 概述 Sublime Text 有非常多的编辑功能,这里只能介绍一点皮毛。 多文本选择 多文本选择让你高效地修改文本,任何的赞美都无法形容它了,原因: 选择一些文本,按下Ctrl+D进行多选,如果想跳过当前项,按下Ctrl+K,Ctrl+D。 误选了按Ctrl+U撤销最后一次选中项。 多行选择合并成一行 Ctrl+L选中单行文本,Ctrl+Shift+L把多行选择变成单行选择的编辑状态。 列选择

  • 复制、粘贴、选择、查找、替换应该是我们在编写代码时最常用的操作了,Android Studio可以让这些操作变得简单和高效。

  • 基础 安装 扩展市场 任务 调试 为什么选用VSCode 版本控制 易用性 与时俱进的编辑体验

  • 因为要编辑项目需要的代码,所以你需要选择一款文本编辑器。不需要在挑选编辑器上花太多时间,一开始,您只需要一款现代的,使用简单的编辑器就行了。用你已经熟悉的,还没谱的话可以使用我推荐的 Atom 编辑器。 Atom Brackets Sublime Visual Studio Code