I am setting up drag & drop in a Java FX application. When I try to grab custom data from the Dragboard
I am getting a java.nio.HeapByteBuffer
instead of an Object
as the JavaDoc stuggest. This byte buffer cannot be cast to my original data type.
I'm working in Scala so the syntax it a tiny bit different but you get the idea. However, perhaps it's because I'm in Scala that I'm getting this HeapByteBuffer
instead of a regular Object
?
Ok, here I'm going to create JavaFX control that can be dragged and dropped. I'm going to attach a String
and a MyObject
; the String
can be retrieved while the MyObject
cannot.
Note, I'm aware there are simpler ways to attach simple strings to the dragboard, this is just an example to show that MyObject
should be retrievable from the dragboard also.
Here I create a custom component which can be dragged.
When it is dragged, two objects are attached to the dragboard: a MyObject
and a String
.
class ToolboxItem
extends Label {
setOnDragDetected(new EventHandler[MouseEvent] {
def handle(event: MouseEvent) {
val dragboard = startDragAndDrop(TransferMode.COPY)
val content = new ClipboardContent()
content.put(DnDTarget.DndString, "sean is cool")
content.put(DnDTarget.DndObject, new MyObject)
dragboard.setContent(content)
event.consume()
}
}
MyObject
is super simple for the sake of getting going:
class MyObject
extends Serilaizable
Now, when I receive a drag event, I want to grab this data from the event.
(also, here I define my DataFormat
)
object DnDTarget {
val DndString = new DataFormat("my.custom.dnd.string")
val DndObject = new DataFormat("my.custom.dnd.object")
}
trait DnDTarget
extends Node {
setOnDragOver(new EventHandler[DragEvent]() {
def handle(event: DragEvent) {
if (valid(event)) {
val dragboard = event.getDragboard
val myString = dragboard.getContent(DnDTarget.DndString)
val myObject = dragboard.getContent(DnDTarget.DndObject)
myString.asInstanceOf[String] // no problem
myObject.asInstanceOf[MyObject] // throws exception
event.acceptTransferModes(TransferMode.COPY)
}
event.consume()
}
})
}
Ok, so, calling myString.asInstanceOf[String]
works fine, I got my String
back. However, myObject.asInstanceOf[MyObject]
throws a ClassCastException
saying:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.nio.HeapByteBuffer cannot be cast to com.example.MyObject
Note: I am not using ScalaFX here, vanilla JavaFX in Scala
I had a similar problem, while trying to implement drag-and-drop in JavaFX 8. When implementing a custom type, I got the same exception during deserialization. And I eventually figured out what the problem was in my case.
It was a bug on my part, where my "MyObject" class actually wasn't deserializable (In my case MyObject extended a supertype that didn't implement Serializable and didn't have a default no-argument constructor, which it must have in such a case). Anyway, after testing that "MyObject" can be serialized / deserialized in a unit test, my JavaFX drag-and-drop started working. As far as I can tell, the problem is this:
JavaFX error handling seems to hide any exceptions during deserialization, and returns the serialized bytebuffer instead of your deserialized object.
It is debatable wether or not this is good error handling.
In your case, you can test this by trying to deserialize the bytebuffer you get from JavaFX yourself using the following java-code:
public static MyObject deserialize(ByteBuffer buffer) {
try {
ByteArrayInputStream is = new ByteArrayInputStream(buffer.array());
ObjectInputStream ois = new ObjectInputStream(is);
MyObject obj = (MyObject) ois.readObject();
return obj;
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
If this method throws an exception, you have your bug. Fix it, and drag and drop should start working again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With