title: JavaFX - Making ListView of custom made objects editable?
body: <p>So I've made a simple to-do list using JavaFX. I wish to make each item in the To-do list editable by simply double clicking it and being able to enter new values for the item. I've been reading through JavaDocs to gain a better understanding of how to do this. I found an example online on how to do this with A listview of type String, but it seems to be more complicated when you have a listview of custom made objects (as is the case with me, where the ListView is of type ToDoItem, a class shown later in this post). Here is what I've tried so far.</p>
  
  <pre><code>  public void initialize()
      {
          listContextMenu = new ContextMenu();
          MenuItem deleteMenuItem = new MenuItem("Delete");
          deleteMenuItem.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
              @Override
              public void handle(ActionEvent event) {
                  TodoItem item = todoListView.getSelectionModel().getSelectedItem();
                  deleteItem(item);
              }
          });
  
          listContextMenu.getItems().addAll(deleteMenuItem);
          todoListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener&lt;TodoItem&gt;() {
              @Override
              public void changed(ObservableValue&lt;? extends TodoItem&gt; 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&lt;TodoItem&gt;() {
              @Override
              public boolean test(TodoItem todoItem) {
                  return true;
              }
          };
  
          wantTodaysItems = new Predicate&lt;TodoItem&gt;() {
              @Override
              public boolean test(TodoItem todoItem) {
                  return todoItem.getDeadline().equals(LocalDate.now());
              }
          };
          filteredList = new FilteredList&lt;TodoItem&gt;(TodoData.getInstance().getTodoitems(), wantAllItems);
          SortedList&lt;TodoItem&gt; sortedList = new SortedList&lt;TodoItem&gt;(filteredList,
                  new Comparator&lt;TodoItem&gt;() {
                      @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&lt;ListView&lt;TodoItem&gt;, ListCell&lt;TodoItem&gt;&gt;() {
              @Override
              public ListCell&lt;TodoItem&gt; call(ListView&lt;TodoItem&gt; lv) {
                  TextFieldListCell&lt;TodoItem&gt; cell = new TextFieldListCell&lt;TodoItem&gt;(){
                      @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&lt;KeyEvent&gt;() {
                      @Override
                      public void handle(KeyEvent event) {
                          if(event.getCode() == KeyCode.ENTER)
                              cell.commitEdit(lv.getSelectionModel().getSelectedItem());
                      }
                  });
  
                  cell.emptyProperty().addListener(
                          (obs, wasEmpty, isNowEmpty) -&gt;
                          {
                              if(isNowEmpty)
                                  cell.setContextMenu(null);
                              else
                                  cell.setContextMenu(listContextMenu);
                          }
                  );
  
                  cell.setConverter(new StringConverter&lt;TodoItem&gt;() {
                      @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&lt;ListView.EditEvent&lt;TodoItem&gt;&gt;() {
              @Override
              public void handle(ListView.EditEvent&lt;TodoItem&gt; e) {
                  todoListView.getItems().set(e.getIndex(), e.getNewValue());
              }
          });
  
      }
  </code></pre>
  
  <p>My ListView is of type ToDoItem. Here is what ToDoItem class looks like:</p>
  
  <pre><code>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();
  }
  }
  </code></pre>
  
  <p>The To-Do list UI itself runs and shows on screen without a problem, and all other functionality works (adding item, deleting item etc..). The only thing im having trouble with is making it editable. </p>
  
  <p>Here is the stack trace I get upon pressing ENTER (line 162 refers to the beginning of the setOnEditCommit method and line 165 refers to the single line of code within the handle method): </p>
  
  <pre><code>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)
  </code></pre>