JavaFX ListView编辑文本字段ListCell

JavaFX ListView编辑文本字段ListCell,java,listview,javafx,javafx-8,Java,Listview,Javafx,Javafx 8,我正在开发一个客户机-服务器解决方案,并且我与连接的客户机有一个列表视图。用户只需在ListView中编辑名称,就可以远程重命名客户端。我已经阅读了很多关于编辑ListView单元格的内容,但是我还没有找到一个好的示例,可以在其中更改类的成员属性。大多数示例都有字符串列表,我认为这不是现代软件开发,如果ListView中的项目不仅仅是字符串 我想做的是更改客户机内部的属性name。 class Client { private String name; public S

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

我想做的是更改客户机内部的属性
name

class Client {
    private String name;
    
    public String getName(){
        return name;
    }
    
    public String setName(String val){
        name = val;
    }
}
我不在乎是否必须将成员
name
实现为JavaFX属性(事实上我已经这样做了,但为了可读性和简单性而忽略了它)

尝试使用TextFieldListCell工厂失败 如果您感兴趣,我已尝试使用TextFieldListCell单元工厂:

this.listViewClients.setCellFactory(TextFieldListCell
 .forListView(new NetworkClientStringConverter(this.clientController)));
但我发现了一些棘手的事情:

  • 每次名称更改时,我都无法访问该对象。显然,我应该做的是创建一个新的客户端并返回它(
    fromString
    )。
    • 通过将客户端控制器传递给
      StringConverter
      ,我解决了这个问题。(这不太好,我更愿意访问我正在更改的客户端。)
  • 尽管如此,这个名字并没有改变

使用标准的
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我认为如果您使用标准的javabean属性而不是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 ;
    }

}
使用此单元实现的SSCCE是

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.application.application;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.ContentDisplay;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.ListView;
导入javafx.scene.control.TextField;
导入javafx.scene.input.KeyCode;
导入javafx.scene.input.KeyEvent;
导入javafx.scene.layout.BorderPane;
导入javafx.stage.stage;
公共类EditableListView扩展了应用程序{
@凌驾
公共无效开始(阶段primaryStage){
ListView ListViewClient=新建ListView();
for(int i=1;i new ClientListCell());
//调试:
按钮调试=新按钮(“显示客户端”);
setOnAction(e->listViewClients.getItems().stream().map(Client::getName).forEach(System.out::println));
BorderPane root=新的BorderPane(ListViewClient,null,null,debug,null);
primaryStage.setScene(新场景(根));
primaryStage.show();
}
公共静态void main(字符串[]args){
发射(args);
}
}

是的,我已经说过了:)是的,对不起,我刚刚注意到:)我不想去cell factory的原因是我显然无法将它轻松地划分为自己的类。只是内联实现它需要很多代码?明天我们将对此进行研究,谢谢James。将其作为一个独立类重写非常简单。当我回到计算机前时,我会更新。仅供参考:我创建了自己的StringConverter类(实际上几乎相同),因为它不是内部的(或lambda),所以我必须将客户机控制器传递给它。也许我可以尝试使用单元格而不是控制器来调整它。你应该能够将单元格的引用传递给它。@ElMac paddling back:当涉及到编辑时,这是一个难题。。。类型是硬编码的(celll必须具有与列表相同的参数类型,从而导致方法/事件携带该类型-无法找到任何方法绕过…)
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);
    }
}
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);
    }
}
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);
    }
}