80
|
Chapter 2, Lists and Combos
#17 Reorder a JList with Drag-and-Drop
HACK
H A C K
#17
Reorder a JList with Drag-and-Drop Hack #17
Let users put things where they want.
You may be so used to immutable lists that the idea of reordering a list with
drag-and-drop seems unnatural. The first time I saw it—rearranging the
order of network devices in Mac OS X to establish a priority (e.g., try Ether-
net, then wireless, then modem)—I thought it was kind of odd. In fact,
Apple felt it necessary to put a label on the list to tell users they could drag-
and-drop the list items to rearrange them. Now that I’m used to it, it’s
totally cool, and I’d like to see it done in more places.
To implement this functionality in a
JList, you basically just have to imple-
ment the full set of AWT drag-and-drop interfaces because the list will be
both the source of the drag and the target of the drop. The other thing you
need to do is to use some cell rendering tricks to provide a visual cue as to
where the drop will occur.
The
ReorderableJList, shown in Example 2-12, is a JList that uses a
DefaultListModel, which is mutable for the obvious reason that it will
need to change in response to drag-and-drops. The bulk of it is concerned
with implementing the drag-and-drop interfaces
DragSourceListener,
DropTargetListener, and DragGestureListener. It has an inner class imple-
menting
Tranferable to hold the item being dropped, although this isn’t
absolutely necessary. I could have just held the dragged item in an instance
variable and
nulled the Transferable in the drag-and-drop calls, but it
doesn’t hurt to do it the nice way.
Figure 2-8. Showing a directory of images with special cell layout
Reorder a JList with Drag-and-Drop #17
Chapter 2, Lists and Combos
|
81
HACK
Example 2-12. A JList that can be reordered with drag-and-drop
public class ReorderableJList extends JList
implements DragSourceListener, DropTargetListener, DragGestureListener {
static DataFlavor localObjectFlavor;
static {
try {
localObjectFlavor =
new DataFlavor (DataFlavor.javaJVMLocalObjectMimeType);
} catch (ClassNotFoundException cnfe) { cnfe.printStackTrace( ); }
}
static DataFlavor[] supportedFlavors = { localObjectFlavor };
DragSource dragSource;
DropTarget dropTarget;
Object dropTargetCell;
int draggedIndex = -1;
public ReorderableJList ( ) {
super( );
setCellRenderer (new ReorderableListCellRenderer( ));
setModel (new DefaultListModel( ));
dragSource = new DragSource( );
DragGestureRecognizer dgr =
dragSource.createDefaultDragGestureRecognizer (this,
DnDConstants.ACTION_MOVE,
this);
dropTarget = new DropTarget (this, this);
}
// DragGestureListener
public void dragGestureRecognized (DragGestureEvent dge) {
System.out.println ("dragGestureRecognized");
// find object at this x,y
Point clickPoint = dge.getDragOrigin( );
int index = locationToIndex(clickPoint);
if (index == -1)
return;
Object target = getModel( ).getElementAt(index);
Transferable trans = new RJLTransferable (target);
draggedIndex = index;
dragSource.startDrag (dge,Cursor.getDefaultCursor( ),
trans, this);
}
// DragSourceListener events
public void dragDropEnd (DragSourceDropEvent dsde) {
System.out.println ("dragDropEnd( )");
dropTargetCell = null;
draggedIndex = -1;
repaint( );
}
82
|
Chapter 2, Lists and Combos
#17 Reorder a JList with Drag-and-Drop
HACK
The constructor creates the list model and sets the cell renderer (an inner
class that will be described soon) and creates a
DragGestureRecognizer to
react to whatever input is judged by the host platform to be a drag (usually
clicking and holding down the mouse button while then moving the mouse).
It also creates a
DragSource and a DropTarget.
When a drag begins,
dragGestureRecognized( ) gets called. Your responsibil-
ity at this point is to start the drag by creating a
Transferable and handing it
to the
DragSource object via startDrag( ). In this case, you can use the
DragGestureEvent to get a Point, from which you can figure out which list
item was clicked on. You can get the
Object from the model, but the
DragSource wants it to be wrapped by a Transferable. Normally,
Transferable is used in drag-and-drop to negotiate with the DropTarget on a
DataFlavor, and it can express the transferred data. Of course, much of that
is irrelevant in this case because the list is already perfectly capable of han-
dling the object, considering the list already had it before the drag began.
For this demo, I’ve created a fairly trivial
Transferable, called
RJLTransferable, which only knows how to represent plain old Java objects
(usually called POJOs).
RJLTransferable is shown in Example 2-13.
public void dragEnter (DragSourceDragEvent dsde) {}
public void dragExit (DragSourceEvent dse) {}
public void dragOver (DragSourceDragEvent dsde) {}
public void dropActionChanged (DragSourceDragEvent dsde) {}
// DropTargetListener events
public void dragEnter (DropTargetDragEvent dtde) {
System.out.println ("dragEnter");
if (dtde.getSource( ) != dropTarget)
dtde.rejectDrag( );
else {
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
System.out.println ("accepted dragEnter");
}
}
public void dragExit (DropTargetEvent dte) {}
// dragOver( ) listed below
// drop( ) listed below
public void dropActionChanged (DropTargetDragEvent dtde) {}
// main( ) method to test - listed below
// RJLTransferable listing below
// ReorderableListCellRendering listing below
}
Example 2-12. A JList that can be reordered with drag-and-drop (continued)

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.