Javafx 如何创建复杂对象的列表视图并允许编辑对象上的字段?

Javafx 如何创建复杂对象的列表视图并允许编辑对象上的字段?,javafx,Javafx,我想要一个Person对象的JavaFXListView。我希望列表仅显示名称并允许编辑名称。在提交对名称的编辑后,还应保留每个对象中的其他字段。您将如何在JavaFX中习惯性地执行此操作 我有下面的代码,这是可行的,但它有点不可靠,因为它有一个StringConverter,它以一种方式将Person转换为Person的名称字符串,然后不进行反向转换,而是依赖list cell CommittedIt方法获取名称字符串并将其设置为适当的Person 代码如下: import javafx.ap

我想要一个Person对象的JavaFXListView。我希望列表仅显示名称并允许编辑名称。在提交对名称的编辑后,还应保留每个对象中的其他字段。您将如何在JavaFX中习惯性地执行此操作

我有下面的代码,这是可行的,但它有点不可靠,因为它有一个StringConverter,它以一种方式将Person转换为Person的名称字符串,然后不进行反向转换,而是依赖list cell CommittedIt方法获取名称字符串并将其设置为适当的Person

代码如下:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;

public class Main extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("My Custom List View");
        ObservableList<Person> people = FXCollections.observableArrayList(
            new Person("John Doe", "123 New York"),
            new Person("Jane Doe", "456 San Francisco")
        );
        ListView<Person> listView = new ListView();
        listView.setCellFactory(new CustomCellFactory());
        listView.setEditable(true);
        listView.setItems(people);
        Scene scene = new Scene(listView,400,300);
        stage.setScene(scene);
        stage.show();
    }

    public static class CustomCellFactory implements Callback<ListView<Person>,ListCell<Person>> {
        @Override
        public ListCell<Person> call(ListView param) {
            TextFieldListCell<Person> cell = new TextFieldListCell() {
                @Override
                public void updateItem(Object item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty && item != null) {
                        System.out.println("updating item: "+item.toString());
                        setText(((Person) item).getName());
                    } else {
                        setText(null);
                    }
                }
                @Override
                public void commitEdit(Object newName) {
                    ((Person)getItem()).setName((String)newName);
                    super.commitEdit(getItem());
                }
            };
            cell.setConverter(new StringConverter() {
                @Override
                public String toString(Object person) {
                    return ((Person)person).getName();
                }
                @Override
                public Object fromString(String string) {
                    return string;
                }
            });
            return cell;
        }
    }

    public static class Person {
        private String name;
        private String address;
        public Person(String name, String address) {
            this.name = name;
            this.address = address;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        public String toString() {
            return name+" at "+address;
        }
    }
}
导入javafx.application.application;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.scene.scene;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.ListView;
导入javafx.scene.control.cell.TextFieldListCell;
导入javafx.stage.stage;
导入javafx.util.Callback;
导入javafx.util.StringConverter;
公共类主扩展应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
public void start(Stage)引发异常{
stage.setTitle(“我的自定义列表视图”);
ObservableList people=FXCollections.observableArrayList(
新人(“约翰·多伊”,“123纽约”),
新人(“简·多伊”,“456旧金山”)
);
ListView ListView=新建ListView();
setCellFactory(新的CustomCellFactory());
listView.setEditable(true);
setItems(人);
场景=新场景(listView,400300);
舞台场景;
stage.show();
}
公共静态类CustomCellFactory实现回调{
@凌驾
公共ListCell调用(ListView参数){
TextFieldListCell单元格=新的TextFieldListCell(){
@凌驾
public void updateItem(对象项,布尔值为空){
super.updateItem(项,空);
如果(!empty&&item!=null){
System.out.println(“更新项:+item.toString());
setText(((Person)item.getName());
}否则{
setText(空);
}
}
@凌驾
public void committedit(对象新名称){
((Person)getItem()).setName((String)newName);
super.committedit(getItem());
}
};
cell.setConverter(新的StringConverter(){
@凌驾
公共字符串到字符串(对象人){
return((Person)Person.getName();
}
@凌驾
公共对象fromString(字符串){
返回字符串;
}
});
返回单元;
}
}
公共静态类人员{
私有字符串名称;
私有字符串地址;
公众人物(字符串名称、字符串地址){
this.name=名称;
this.address=地址;
}
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共字符串getAddress(){
回信地址;
}
公共无效设置地址(字符串地址){
this.address=地址;
}
公共字符串toString(){
在“+地址处返回名称+”;
}
}
}

TextFieldListCell
只是
ListCell
的一个方便实现,它为列表单元格提供了最常见的编辑形式(即,如果列表中的项目是
String
s,或易于与字符串进行转换的对象)。您经常会发现需要进行更具体的编辑(例如,您经常希望使用
TextFormatter
)过滤编辑文本字段中允许的文本),在这种情况下,您只需自己实现
ListCell
。我认为在这种情况下,总的来说,从头开始实现
ListCell
更有意义

您似乎可以使用以下命令强制
TextFieldListCell
用于此用例:

    listView.setCellFactory(lv -> {
        TextFieldListCell<Person> cell = new TextFieldListCell<Person>();
        cell.setConverter(new StringConverter<Person>() {
            @Override
            public String toString(Person person) {
                return person.getName();
            }
            @Override
            public Person fromString(String string) {
                Person person = cell.getItem();
                person.setName(string);
                return person ;
            }
        });
        return cell;
    });
如果您可能经常需要此类功能,您可以轻松创建一个可重用类:

import java.util.function.BiFunction;
import java.util.function.Function;

import javafx.scene.control.ListCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class EditingListCell<T> extends ListCell<T> {
    private final TextField textField ;
    private final Function<T, String> propertyAccessor ;

    public EditingListCell(Function<T, String> propertyAccessor, BiFunction<String, T, T> updater) {
        this.propertyAccessor = propertyAccessor ;
        this.textField = new TextField();

        textField.setOnAction(e -> {
            T newItem = updater.apply(textField.getText(), getItem());
            commitEdit(newItem);
        });
        textField.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
            }
        });
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            setGraphic(null);
        } else if (isEditing()) {
            textField.setText(propertyAccessor.apply(item));
            setText(null);
            setGraphic(textField);
        } else {
            setText(propertyAccessor.apply(item));
            setGraphic(null);
        }
    }

    @Override
    public void startEdit() {
        super.startEdit();
        textField.setText(propertyAccessor.apply(getItem()));
        setText(null);
        setGraphic(textField);       
        textField.selectAll();
        textField.requestFocus();
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(propertyAccessor.apply(getItem()));
        setGraphic(null);
    }

    @Override
    public void commitEdit(T item) {
        super.commitEdit(item);
        getListView().getItems().set(getIndex(), item);
        setText(propertyAccessor.apply(getItem()));
        setGraphic(null);        
    }
}
import java.util.function.BiFunction;
导入java.util.function.function;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.TextField;
导入javafx.scene.input.KeyCode;
导入javafx.scene.input.KeyEvent;
公共类EditingListCell扩展ListCell{
私有最终文本字段文本字段;
私有最终功能属性Accessor;
公共编辑ListCell(函数属性Accessor、双函数更新程序){
this.propertyAccessor=propertyAccessor;
this.textField=新的textField();
textField.setOnAction(e->{
T newItem=updater.apply(textField.getText(),getItem());
提交(新项目);
});
textField.addEventFilter(KeyEvent.KEY_已发布,e->{
if(例如getCode()==KeyCode.ESCAPE){
取消编辑();
}
});
}
@凌驾
受保护的void updateItem(T项,布尔值为空){
super.updateItem(项,空);
if(空| |项==null){
setText(空);
设置图形(空);
}else if(isEditing()){
textField.setText(propertyAccessor.apply(项目));
setText(空);
设置图形(文本字段);
}否则{
setText(propertyAccessor.apply(项目));
设置图形(空);
}
}
@凌驾
公开作废已启动IT(){
super.startEdit();
setText(propertyAccessor.apply(getItem());
import java.util.function.BiFunction;
import java.util.function.Function;

import javafx.scene.control.ListCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class EditingListCell<T> extends ListCell<T> {
    private final TextField textField ;
    private final Function<T, String> propertyAccessor ;

    public EditingListCell(Function<T, String> propertyAccessor, BiFunction<String, T, T> updater) {
        this.propertyAccessor = propertyAccessor ;
        this.textField = new TextField();

        textField.setOnAction(e -> {
            T newItem = updater.apply(textField.getText(), getItem());
            commitEdit(newItem);
        });
        textField.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
            }
        });
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            setGraphic(null);
        } else if (isEditing()) {
            textField.setText(propertyAccessor.apply(item));
            setText(null);
            setGraphic(textField);
        } else {
            setText(propertyAccessor.apply(item));
            setGraphic(null);
        }
    }

    @Override
    public void startEdit() {
        super.startEdit();
        textField.setText(propertyAccessor.apply(getItem()));
        setText(null);
        setGraphic(textField);       
        textField.selectAll();
        textField.requestFocus();
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(propertyAccessor.apply(getItem()));
        setGraphic(null);
    }

    @Override
    public void commitEdit(T item) {
        super.commitEdit(item);
        getListView().getItems().set(getIndex(), item);
        setText(propertyAccessor.apply(getItem()));
        setGraphic(null);        
    }
}
listView.setCellFactory(lv -> new EditingListCell<>(
        Person::getName,
        (text, person) -> {
            person.setName(text);
            return person ;
        })
);