In JavaFx v17, JDK v17, Maven, IntelliJ:
A Listview<TokenWord> (=TOKListView) is in each row of a Listview<Signification> (=SIGListView). When I am editing a cell of the TOKListView I make a commitEdit() call in the startEdit() section, then I get an Error class TokenWord cannot be cast to class Signification
. The SIGListView should not, but will do an updateItem(), but why?
Main.class // ReprEx
public class Main extends Application {
public static void main(String[] args) {
Application.launch(Main.class);
}
public class Signification {
ArrayList<TokenWord> tokenWords;
public Signification(ArrayList<TokenWord> tokenWords) {
this.tokenWords = tokenWords;
}
public ArrayList<TokenWord> getTokenWords() {
return tokenWords;
}
}
public class TokenWord {
String Text;
public TokenWord(String text) {
Text = text;
}
public String getText() {
return Text;
}
}
final ListView<Signification> significationListView = new ListView<>();
final ArrayList<Signification> significations = new ArrayList<>();
@Override
public void start(Stage stage) {
significations.add(
new Signification(new ArrayList<TokenWord>(Arrays.asList(
new TokenWord("One")
))));
significationListView.setCellFactory(significationListView -> {
ListCell<Signification> siCell = new ListCell<Signification>() {
//PROBLEM AREA ---- START --------------------------------------
//########## PROBLEM CONSUMED (of PROBLEM FIRED see below) #########
@Override
public void updateItem(Signification sigItem, boolean empty) {
super.updateItem(sigItem, empty);
if ((!empty) && (sigItem != null)) {
ListView<TokenWord> tokenWordListView = new ListView<TokenWord>();
tokenWordListView.setEditable(true);
tokenWordListView.setOrientation(Orientation.HORIZONTAL);
tokenWordListView.setCellFactory(twListView -> {
// each Cell a new Listview
ListCell<TokenWord> twCell = new ListCell<TokenWord>() {
@Override
public void updateItem(TokenWord twItem, boolean empty) {
super.updateItem(twItem, empty);
if ((!empty) && (twItem != null)) setText(twItem.getText());
}
@Override
public void startEdit() {
if (!isEditable() || !getListView().isEditable()) return;
super.startEdit();
if (isEditing()) {
setText("");
TextField textField = new TextField(getItem().getText());
textField.setOnAction(actionEvent -> {
//########## PROBLEM FIRED ###########
commitEdit(new TokenWord(textField.getText()));
});
setGraphic(textField);
} else setGraphic(null);
}
};
return twCell;
});
tokenWordListView.getItems().addAll(sigItem.getTokenWords());
setGraphic(tokenWordListView);
} else {
setGraphic(null);
}
}
//PROBLEM AREA ---- END --------------------------------------
};
siCell.setPrefHeight(40);
return siCell;
});
significationListView.getItems().addAll(significations);
stage.setScene(new Scene(new StackPane(significationListView), 400, 150));
stage.show();
}
}
Errormessage:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class org.example.TheApp$TokenWord cannot be cast to class org.example.TheApp$Signification (org.example.TheApp$TokenWord and org.example.TheApp$Signification are in unnamed module of loader 'app')
at org.example.TheApp$1.updateItem(TheApp.java:61)
at javafx.scene.control.ListCell.updateItem(ListCell.java:481)
at javafx.scene.control.ListCell.lambda$new$2(ListCell.java:168)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
at javafx.collections.ModifiableObservableListBase.set(ModifiableObservableListBase.java:170)
at javafx.scene.control.ListView.lambda$new$1(ListView.java:375)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
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:8792)
at javafx.scene.control.ListCell.commitEdit(ListCell.java:390)
at org.example.TheApp$1$1.lambda$startEdit$0(TheApp.java:94)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
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.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:8792)
at com.sun.javafx.scene.control.behavior.TextFieldBehavior.fire(TextFieldBehavior.java:154)
at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.lambda$keyMapping$62(TextInputControlBehavior.java:332)
at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
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.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:4105)
at javafx.scene.Scene.processKeyEvent(Scene.java:2156)
at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2630)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:218)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:150)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:250)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:249)
at com.sun.glass.ui.View.handleKeyEvent(View.java:548)
at com.sun.glass.ui.View.notifyKey(View.java:972)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class org.example.TheApp$TokenWord cannot be cast to class org.example.TheApp$Signification (org.example.TheApp$TokenWord and org.example.TheApp$Signification are in unnamed module of loader 'app')
at org.example.TheApp$1.updateItem(TheApp.java:61)
at javafx.scene.control.ListCell.updateItem(ListCell.java:481)
at javafx.scene.control.ListCell.indexChanged(ListCell.java:337)
at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:120)
at javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1807)
at javafx.scene.control.skin.VirtualFlow.addTrailingCells(VirtualFlow.java:2191)
at javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1327)
at javafx.scene.Parent.layout(Parent.java:1207)
at javafx.scene.Parent.layout(Parent.java:1214)
at javafx.scene.Parent.layout(Parent.java:1214)
at javafx.scene.Scene.doLayoutPass(Scene.java:579)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2515)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:420)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:450)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:353)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:833)
CodePudding user response:
The error occurs because editing is controlled via events and we have a ListView with a cell containing a ListView:
- a commit is delivered along the usual event dispatch chain
- the commit is fired from the editing cell (the one containing the textField) to the inner listView
- the default commit handler of the inner listView handles the event by saving the edited value
- then it bubbles up to the commit handler of the outer listView where it causes the class cast exception
The last happens because the default commit handler does not consume the event. The solution is to consume it.
Some code (for clarity extracted into a separate class, could just as well be done inline)
public class OuterCell extends ListCell<Signification> {
ListView<TokenWord> tokenWordListView;
{
tokenWordListView = new ListView<TokenWord>();
tokenWordListView.setEditable(true);
tokenWordListView.setOrientation(Orientation.HORIZONTAL);
// grab default commit handler
EventHandler<EditEvent<TokenWord>> handler = tokenWordListView.getOnEditCommit();
tokenWordListView.setOnEditCommit(t -> {
// let default handler save the edited value
handler.handle(t);
// consume to prevent dispatching the event to the outer list
t.consume();
});
// use TextFieldListCell for inner cell
StringConverter<TokenWord> converter = new StringConverter<>() {
@Override
public String toString(TokenWord token) {
return token != null ? token.getText() : "";
}
@Override
public TokenWord fromString(String string) {
return new TokenWord(string);
}
};
tokenWordListView.setCellFactory(TextFieldListCell.forListView(converter));
setPrefHeight(40);
}
@Override
public void updateItem(Signification sigItem, boolean empty) {
super.updateItem(sigItem, empty);
if ((!empty) && (sigItem != null)) {
tokenWordListView.getItems().setAll(sigItem.getTokenWords());
setGraphic(tokenWordListView);
} else {
setGraphic(null);
}
}
}