【发布时间】:2018-06-20 14:53:39
【问题描述】:
所以我使用 JavaFX 制作了一个简单的待办事项列表。我希望通过简单地双击它并能够为该项目输入新值来使待办事项列表中的每个项目都可编辑。我一直在阅读 JavaDocs 以更好地理解如何做到这一点。我在网上找到了一个关于如何使用 String 类型的列表视图执行此操作的示例,但是当您有自定义对象的列表视图时,它似乎更复杂(就像我的情况一样,ListView 的类型是 ToDoItem,一个这篇文章后面显示的类)。这是我迄今为止尝试过的。
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);
}
}
@Override
public void commitEdit(TodoItem newValue) {
super.commitEdit(newValue);
}
};
cell.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ENTER)
cell.commitEdit(lv.getSelectionModel().getSelectedItem());
}
});
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) {
todoListView.getItems().set(e.getIndex(), e.getNewValue());
}
});
}
我的 ListView 是 ToDoItem 类型。下面是 ToDoItem 类的样子:
public class TodoItem {
private SimpleStringProperty shortDescription;
private SimpleStringProperty details;
private LocalDate deadline;
public TodoItem(String shortDescription, String details, LocalDate deadline) {
this.shortDescription = new SimpleStringProperty(shortDescription);
this.details = new SimpleStringProperty(details);
this.deadline = deadline;
}
public String getShortDescription() {
return shortDescription.get();
}
public void setShortDescription(String shortDescription) {
this.shortDescription.set(shortDescription);
}
public String getDetails() {
return details.get();
}
public void setDetails(String details) { this.details.set(details); }
public LocalDate getDeadline() {
return deadline;
}
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
@Override
public String toString() {
return shortDescription.get();
}
}
待办事项列表 UI 本身可以正常运行并在屏幕上显示,并且所有其他功能都可以正常工作(添加项目、删除项目等)。我唯一遇到的问题是让它可编辑。
这是我按 ENTER 后得到的堆栈跟踪(第 162 行指的是 setOnEditCommit 方法的开头,第 165 行指的是句柄方法中的单行代码):
Exception in thread "JavaFX Application Thread" java.lang.UnsupportedOperationException
at java.util.AbstractList.set(AbstractList.java:132)
at com.arslansana.todolist.Controller$7.handle(Controller.java:165)
at com.arslansana.todolist.Controller$7.handle(Controller.java:162)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at javafx.scene.control.ListCell.commitEdit(ListCell.java:378)
at com.arslansana.todolist.Controller$6$1.commitEdit(Controller.java:122)
at com.arslansana.todolist.Controller$6$1.commitEdit(Controller.java:104)
at javafx.scene.control.cell.CellUtils.lambda$createTextField$615(CellUtils.java:248)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at com.sun.javafx.scene.control.behavior.TextFieldBehavior.fire(TextFieldBehavior.java:179)
at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:178)
at com.sun.javafx.scene.control.behavior.BehaviorBase.callActionForEvent(BehaviorBase.java:218)
at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callActionForEvent(TextInputControlBehavior.java:127)
at com.sun.javafx.scene.control.behavior.BehaviorBase.lambda$new$74(BehaviorBase.java:135)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$KeyHandler.process(Scene.java:3964)
at javafx.scene.Scene$KeyHandler.access$1800(Scene.java:3910)
at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2040)
at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2501)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:216)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:148)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$353(GlassViewEventHandler.java:247)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:246)
at com.sun.glass.ui.View.handleKeyEvent(View.java:546)
at com.sun.glass.ui.View.notifyKey(View.java:966)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
【问题讨论】:
-
您的代码对我来说工作得很好,完全一样(除了删除您评论的行是不正确的)。实际问题是什么?你能解释一下正在发生或没有发生的事情与你所期望的不同,并扩展代码(应该只需要多几行)以形成minimal reproducible example吗?
-
“工作正常”是指它是可编辑的,并且编辑会以您定义的方式传播回支持列表。
-
当我删除给出错误的行时,它运行正常。如中,GUI 待办事项列表窗口打开显示所有待办事项,它们是可点击的,显示描述,上下文菜单工作正常等。但是,它对我来说不可编辑。双击列表视图中的项目不会执行任何操作。我无法理解您是如何编辑列表视图的。
-
我已经编辑了我的初始帖子以展示我的整个初始化方法。我已经评论了我试图使列表视图可编辑的代码部分。
-
呃,你将提供编辑的单元工厂替换为另一个不可编辑的单元工厂。
标签: java listview user-interface javafx