Web Start and source.
I ran across this nice effect for indicating a drop target in a JList. The ghosted image of an item dragged from the list appears to push aside other entries as it moves through the list of items. A very simple method results in very smooth animation that can span several cells at once. The basic idea is to maintain a list of current positions for all cells, and gradually adjust those positions to match what their final positions would be with a proposed insertion. The animation consists of moving half the distance from the current to the final position (rounding up the final pixel difference). The problem with the sample implementation is that it requires tweaks to both the list model and its LAF UI.
I thought it would be nice to be able to apply this effect to any list without having to replace the LAF UI. Since lists render themselves cell by cell anyway, it shouldn't be that hard to just trick the list into painting each cell where we want it, instead of in its default location.
I focused on just handling an internal drag, but the API on the smoother could easily be tweaked to accept native drags from external sources.
First, to separate the functionality from user triggers, I decided to put the mouse event handling into the demo code. I've found that in UI testing, it's almost always beneficial to be able to drive bits of the UI from direct programmatic methods (would that Swing followed this pattern, so it would be easier to test). The mouse listener decides when to start, update, and end the drag, and calls in to the smoother's corresponding methods.
The smoother itself is a decorator which performs the painting of individual cells by having the original list paint itself fully and then clipping out the unwanted stuff. This could be optimized by just asking for the rendering components, but that potentially omits additional decoration provided by the LAF. So I locate the cell I want within the full list as painted by the LAF, and copy that image to its "floating" location.
// Paint the background for the insertion point
Rectangle b = getDecorationBounds();
g.setColor(list.getBackground());
g.fillRect(b.x, b.y, b.width, b.height);
for (int i=0;i < list.getModel().getSize();i++) {
if (i == draggedIndex)
continue;
Rectangle r = getCurrentCellBounds(i);
Graphics g2 = g.create(r.x, r.y, r.width, r.height);
Rectangle r2 = list.getCellBounds(i, i);
((Graphics2D)g2).translate(0, -r2.y);
list.paint(g2);
}
The Graphics object is set up to paint only in the desired area, then translated such that the JList will paint the desired cell into that area. We skip the index being dragged, since we want to handle that separately.
To draw the ghosted image, I simply used another decorator that uses the JList to render the dragged item. As the insertion location is updated by the mouse listener, the ghost image adjusts its vertical drawing position (being careful to clamp the vertical bounds to the start and end of the list).
The insertion point could use something more fancy if needed, like using the cell renderer or explicitly painting the rectangle. Just leaving the background exposed works well in this case, though.
A timer task periodically moves each cell's current location toward its final location, and triggers a repaint if any of the locations actually changes. This should be optimized to track a smaller range of cells (like only those visible).
The basic calculation of a cell's desired position simply calculates its normal position, then adjusts for any preceding dragged item (to be removed) and any preceding insertion point (to be inserted). The current locations start off with no dragged item or insertion point, and gradually float to their final positions based on a dragged item and possibly changing insertion point. Note that if you rapidly drag across several items, they all will float smoothly even as their destination changes.