I tried to fix this issue for the past week but somehow I cant seem to find a solution. There is not a lot of information about this topic so its hard to find examples or code to look at.
What I have here is a JList which uses a custom TransferHandler that creates a custom Transferable, for reference here's the code of the involved classes:
Transferable:
package org.dinhware.swing.special;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas
* Date: 20.10.2017
* Alias: Dinh
* Time: 20:03
*/
public class GenericTransferable<T> implements Transferable {
static DataFlavor FLAVOR;
private T object;
GenericTransferable(T object) {
GenericTransferable.FLAVOR = new DataFlavor(object.getClass(), object.getClass().getCanonicalName());
this.object = object;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{FLAVOR};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(FLAVOR);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return object;
}
}
TransferHandler:
package org.dinhware.swing.special;
import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas
* Date: 19.10.2017
* Alias: Dinh
* Time: 18:54
*/
@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {
@Override
protected Transferable createTransferable(JComponent component) {
JList<T> list = (JList<T>) component;
index = list.getSelectedIndex();
T transferredObject = list.getSelectedValue();
return new GenericTransferable<>(transferredObject);
}
@Override
public boolean canImport(TransferSupport info) {
return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
}
@Override
public int getSourceActions(JComponent c) {
return MOVE;
}
@Override
public boolean importData(TransferSupport info) {
if (!canImport(info)) {
return false;
}
JList<Object> target = (JList<Object>) info.getComponent();
JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
int index = dl.getIndex();
int max = listModel.getSize();
if (index < 0 || index > max)
index = max;
addIndex = index;
try {
Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
listModel.add(index, object);
target.addSelectionInterval(index, index);
return moveAllowed = true;
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
if (moveAllowed)
cleanup(c, action == MOVE, false);
}
private void cleanup(JComponent component, boolean remove, boolean bin) {
if (remove && index != -1) {
JList<T> source = (JList<T>) component;
DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
int removeAt = index > addIndex ? index + 1 : index;
model.remove(bin ? removeAt - 1 : removeAt);
}
index = -1;
addIndex = -1;
moveAllowed = false;
}
private int index = -1;
private int addIndex = -1;
private boolean moveAllowed = false;
}
HBin
package org.dinhware.swing.child;
import org.dinhware.swing.special.HListItemTransferHandler;
import javax.swing.*;
/**
* Created by: Niklas
* Date: 20.10.2017
* Alias: Dinh
* Time: 19:57
*/
public class HBin<T> extends HImageLabel {
public HBin(String text, Icon image, int distance) {
super(text, image, distance);
setTransferHandler(new HListItemTransferHandler<T>());
}
}
And a visualization of how it should work, sadly the container always disappears even when not dragged onto the HBin Container. I thought it was working the entire time until I accidentally moved it outside of my Frame and it still disappeared. The Code above only allows dragging/dropping inside of the List which is intended.
My Question is how to add the functionality to properly make a Container only disappear when dragged onto the HBin Container
The first part of Code I used was this
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
if (moveAllowed) cleanup(c, action == MOVE, false);
else try {
if (data.getTransferData(GenericTransferable.FLAVOR) instanceof RewardItem) {
cleanup(c, true, true);
}
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
My logic behind it was that both the List and HBin shared the same Type (RewardItem) which I could compare, later I realized (after making a more generic version of that method) that data will always be of the type RewardItem and will always result in a cleanup call. This results in the bug I am currently still facing.
The approach I took earlier today really made me question my mind and also made me do this post. I added a boolean to the TransferHandler called bin which was false by default. After the canImport check in importData I added bin = info.getComponent() instanceof HBin which I thought should work. But this field always stood false. I went ahead and added a log for it
System.out.println("IMPORT");
if (info.getComponent() instanceof HBin) {
System.out.println("bin");
return bin = true;
}
It ended up printing IMPORT followed by bin. After importData exportData is called in which I then logged the value of bin, which for whatever reason was now false again. Meanwhile the moveAllowed field seems to change.
This was my full modified TransferHandler
package org.dinhware.swing.special;
import org.dinhware.swing.child.HBin;
import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas Date: 19.10.2017 Alias: Dinh Time: 18:54
*/
@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {
@Override
protected Transferable createTransferable(JComponent component) {
System.out.println("CREATE");
JList<T> list = (JList<T>) component;
index = list.getSelectedIndex();
T transferredObject = list.getSelectedValue();
return new GenericTransferable<>(transferredObject);
}
@Override
public boolean canImport(TransferSupport info) {
return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
}
@Override
public int getSourceActions(JComponent c) {
System.out.println("ACTION");
return MOVE;
}
@Override
public boolean importData(TransferSupport info) {
System.out.println("IMPORT");
if (!canImport(info)) {
return false;
}
if (info.getComponent() instanceof HBin) {
System.out.println("bin");
return bin = true;
}
JList<Object> target = (JList<Object>) info.getComponent();
JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
int index = dl.getIndex();
int max = listModel.getSize();
if (index < 0 || index > max)
index = max;
addIndex = index;
try {
Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
listModel.add(index, object);
target.addSelectionInterval(index, index);
return moveAllowed = true;
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
System.out.println("EXPORT " + moveAllowed + "/" + bin);
if (moveAllowed)
cleanup(c, action == MOVE, false);
else
cleanup(c, true, true);
}
private void cleanup(JComponent component, boolean remove, boolean bin) {
System.out.println("CLEAN");
if (remove && index != -1) {
JList<T> source = (JList<T>) component;
DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
int removeAt = index > addIndex ? index + 1 : index;
model.remove(bin ? removeAt - 1 : removeAt);
}
index = -1;
addIndex = -1;
moveAllowed = false;
}
private int index = -1;
private int addIndex = -1;
private boolean moveAllowed = false, bin = false;
}
When moving inside of the List everything works fine (prints)
ACTION
CREATE
IMPORT
EXPORT true/false
CLEAN
But when dropping onto the HBin Container I cant explain whats going on (prints)
ACTION
CREATE
IMPORT
bin
EXPORT false/false
I am farily sure that it should be false/true
Now I am stuck, not able to make the Container only disappear when dropped onto HBin whilst also being confused about the field value not changing when it clearly logs that it has been set to true.
Please.. help...

Drag'n'Drop is complicated and it's not helped by the fact that there are at least two ways to do it.
D'n'D revolves around the idea of "wrapping" an object up in a "transferable" package, which can be "imported" via a number of different means (i.e.
DataFlavors)So, in this example, I've focused only on removing items from the
JList, to do this, I created aTrashobject which actually maintains a reference to the item to be removed (I also created aListTrashobject to demonstrate at least one way you could pass more information)This object is then wrapped in a
TrashTransferablewhen a drag occurs on theJListThe main reason for having a
Trashobject, it it allows theDataFlavorto be standardised. The "trash can" only cares aboutTrashobjects, nothing else. This is especially important if you have moreTransferHandlers doing more operationsOne other thing I did was create two
TransferHandlers. One for the "trash can" and one for theJList, the main reason for this is it isolates the functionality each handler wants to perform and reduces the complexity, as you're not also trying to determine which object is trying to perform which operation.The example also has another component which is not doing much at all, so it can reject the drop operation.
If you have another components using
TransferHandlers, then those need to reject theTrashTransferable.FLAVOR