JavaFX对SetOnEditCommit方法感到困惑

时间:2018-02-10 23:17:35

标签: listview javafx

我使用JavaFX创建了一个简单的待办事项列表应用程序。在做了一些研究之后,我想出了如何使ListView可编辑(这样双击单元格就可以让你更改里面的文本)。到目前为止一切正常,但我对setOnEditCommit方法在我的代码中扮演什么角色感到困惑。这是我的整个初始化方法,setOnEditCommit位于底部。

    public void initialize()
{
    listContextMenu = new ContextMenu();
    MenuItem deleteMenuItem = new MenuItem("Delete");
    deleteMenuItem.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            TodoItem item = todoListView.getSelectionModel().getSelectedItem();
            deleteItem(item);
        }
    });

    listContextMenu.getItems().addAll(deleteMenuItem);
    todoListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TodoItem>() {
        @Override
        public void changed(ObservableValue<? extends TodoItem> observable, TodoItem oldValue, TodoItem newValue) {
            if(newValue != null)
            {
                TodoItem item = todoListView.getSelectionModel().getSelectedItem();
                itemDetailsTextArea.setText(item.getDetails());
                DateTimeFormatter df = DateTimeFormatter.ofPattern("MMMM dd, yyyy");
                deadlineLabel.setText(item.getDeadline().format(df));
            }
        }
    });

    wantAllItems = new Predicate<TodoItem>() {
        @Override
        public boolean test(TodoItem todoItem) {
            return true;
        }
    };

    wantTodaysItems = new Predicate<TodoItem>() {
        @Override
        public boolean test(TodoItem todoItem) {
            return todoItem.getDeadline().equals(LocalDate.now());
        }
    };
    filteredList = new FilteredList<TodoItem>(TodoData.getInstance().getTodoitems(), wantAllItems);
    SortedList<TodoItem> sortedList = new SortedList<TodoItem>(filteredList,
            new Comparator<TodoItem>() {
                @Override
                public int compare(TodoItem o1, TodoItem o2) {
                    return o1.getDeadline().compareTo(o2.getDeadline());
                }
            });

    //todoListView.setItems(TodoData.getInstance().getTodoitems());
    todoListView.setItems(sortedList);
    todoListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
    todoListView.getSelectionModel().selectFirst();

    todoListView.setEditable(true);

    todoListView.setCellFactory(new Callback<ListView<TodoItem>, ListCell<TodoItem>>() {
        @Override
        public ListCell<TodoItem> call(ListView<TodoItem> lv) {
            TextFieldListCell<TodoItem> cell = new TextFieldListCell<TodoItem>(){
                @Override
                public void updateItem(TodoItem item, boolean empty) {
                    super.updateItem(item, empty);

                    if(empty){
                        setText(null);
                    } else {
                        setText(item.getShortDescription());
                        if(item.getDeadline().isBefore(LocalDate.now().plusDays(1)))
                            setTextFill(Color.RED);
                        else if(item.getDeadline().equals(LocalDate.now().plusDays(1)))
                            setTextFill(Color.BROWN);
                    }
                }

            };

            cell.emptyProperty().addListener(
                    (obs, wasEmpty, isNowEmpty) ->
                    {
                        if(isNowEmpty)
                            cell.setContextMenu(null);
                        else
                            cell.setContextMenu(listContextMenu);
                    }
            );

            cell.setConverter(new StringConverter<TodoItem>() {
                @Override
                public String toString(TodoItem object) {
                    return object.toString();
                }

                @Override
                public TodoItem fromString(String string) {
                    cell.getItem().setShortDescription(string);
                    return cell.getItem();
                }
            });
            return cell;
        }
    });

    // this is the method where the source of the exception is being reported
    todoListView.setOnEditCommit(new EventHandler<ListView.EditEvent<TodoItem>>() {
        @Override
        public void handle(ListView.EditEvent<TodoItem> e) {
        }
    });
}

现在我很困惑,因为注释掉setOnEditCommit方法会导致异常。显然我需要它。但是,重写的句柄函数里面什么也没做,里面没有代码行。那么为什么有必要保留这个以及它做什么呢?这叫什么时候?

请记住我的待办事项清单功能就好了。提交并保存编辑,以便您在下次运行时可以看到它们。我觉得我对某些事情产生了极大的误解。

2 个答案:

答案 0 :(得分:0)

提交并修改TextFieldListCell这样的作品:

  • 如果设置了onEditCommit处理程序,则ListView会假定此处理程序用于处理编辑。

  • 如果未设置onEditCommit处理程序,则使用转换器将TextField内容转换为项目的结果将设置为单元格中项目的索引。

由于您使用SortedList ListView调用set会产生异常(该列表只是一个不同ObservableList的视图,无法直接修改)。设置onEditCommit处理程序会阻止ListView调用SortedList.set,从而防止出错。

答案 1 :(得分:0)

根据documentation

  

默认情况下,ListView编辑提交处理程序为非null,其默认处理程序尝试覆盖当前正在编辑的行中的项的属性值。 ...非常重要的是要注意,如果您使用自己的EventHandler调用setOnEditCommit(javafx.event.EventHandler),那么您将删除默认处理程序。

当您提交修改时,TextFieldListCell会触发ListView.EditEvent事件,其newValue是通过将文本字段的文本传递给转换器的fromString()方法而创建的值。

默认的onEditCommit()处理程序获取该值并将其设置在列表中。即它做了一些事情

event.getSource().getItems().set(event.getIndex(), event.getNewValue());

由于您使用不可修改的列表(SortedList)作为ListView的项目列表,因此尝试在其中设置值会引发{{1} }。

通过显式设置UnsupportedOperationException处理程序,删除默认处理程序,因此异常消失。由于您的onEditCommit实际上已经完成了更新数据的工作(纯粹主义者可能认为这违反了关注点的分离),因此您实际上不需要在此处理程序中执行任何操作。

如果您的转换器没有为您修改该项目(例如,通过返回新的converter):

TodoItem

然后你需要你的处理程序更新支持列表,你可以使用

cell.setConverter(new StringConverter<TodoItem>() {
    @Override
    public String toString(TodoItem object) {
        // shouldn't this return the property that you're editing?
        return object.getShortDescription();
    }

    @Override
    public TodoItem fromString(String string) {
        TodoItem currentItem = cell.getItem();
        TodoItem editedItem = new TodoItem();
        editedItem.setShortDescription(string);
        editedItem.setDeadline(currentItem.getDeadline());
        // etc...
        return editedItem ;
    }
});

(假设todoListView.setOnEditCommit(e -> { int indexInSortedList = e.getIndex(); int indexInFilteredList = sortedList.getSourceIndex(indexInSortedList); int indexInOriginalList = filteredList.getSourceIndex(indexInFilteredList); TodoData.getInstance().getTodoitems().set(indexInOriginalList, e.getNewValue()); }); 返回的列表是可修改的)。