Dragging and dropping with gwt-dnd
February 14th, 2008 - Written by in Using GWT
Today, we are going to take a look at adding drag-and-drop to our GWT applications. I’ve seen quite a few solutions for adding drag-and-drop, including a few tutorials that show how roll your own solution. But why reinvent the wheel when there is a perfectly good drag and drop library like ? This library by Fred Sauer provides a whole host of cool features. I took some time to play with it this past week and I will show you how to get started using the library by creating a simple shopping cart example.
To install, download the latest gwt-dnd jar file, add it to your build path and add the following line to your GWT application module file.
<inherits name='com.allen_sauer.gwt.dnd.gwt-dnd'/>
In this demo, we’re going to allow users to drop books into a shopping cart. You can take a sneak peak of what I’m going to build here. The first domain object I will create is the Book class.
()
public class Book extends Composite implements SourcesMouseEvents { private String title; private String imgUrl; private BigDecimal price; private Image bookImage; private VerticalPanel mainPanel; public Book(String title, BigDecimal price, String imgUrl) { this.title = title; this.price = price; this.imgUrl = imgUrl; this.mainPanel = new VerticalPanel(); initWidget(mainPanel); bookImage = new Image(imgUrl); mainPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); mainPanel.add(bookImage); mainPanel.add(new Label(title)); mainPanel.add(new Label("$"+price)); mainPanel.addStyleName("book"); public void addMouseListener(MouseListener listener) { bookImage.addMouseListener(listener); } ... }
Book is a GWT composite widget that will display the book’s title, image, and price. Notice that it implements the SourcesMouseEvents
interface. This is required to make the book draggable. In my implementation, I attach a MouseListener
to the image field because the GWT Image widget already implements this interface and it will allow users to use the image as a drag handle to move the book around.
Dragging is handled by implementing the DragController
interface. In this demo I am extending PickupDragController
, which allows me to move the books around a specified boundary panel. By default, the dragged widget will disappear when dropped on the drop target. I don’t want this behavior so I’ll use a drag proxy instead. This means I can drag a copy of the book’s image and leave the original book widget alone. To achieve this, I set setBehaviorDragProxy()
to true and override PickupDragController
’s newDragProxy()
method.
()
PickupDragController dragController = new PickupDragController(containingPanel, false) { protected Widget newDragProxy(DragContext context) { AbsolutePanel container = new AbsolutePanel(); DOM.setStyleAttribute(container.getElement(), "overflow", "visible"); for (Iterator iterator = context.selectedWidgets.iterator(); iterator.hasNext();) { Widget widget = (Widget) iterator.next(); Book book = (Book)widget; container.add(new Image(book.getImageUrl())); } return container; } };
Then I make Book widgets draggable by calling makeDraggable
on the drag controller. Here, we pass to the method the book object (the draggable widget) and its image (the drag handle).
()
FlowPanel flowPanel = new FlowPanel(); flowPanel.addStyleName("flowPanel"); for (int i = 0; i < books.length; i++) { Book book = books[i]; dragController.makeDraggable(book, book.getImage()); flowPanel.add(book); }
For the drop part of the demo, I created a object which serves as the drop target. Dropping is handled by a DropController
object. The library comes with several implementations, and I chose to extend the SimpleDropController
class.
()
public class CartDropController extends SimpleDropController { private ShoppingCart cart; public CartDropController(Widget dropTarget) { super(dropTarget); cart = (ShoppingCart)dropTarget; } public void onDrop(DragContext context) { super.onDrop(context); Book book = (Book)context.draggable; cart.add(book); } public void onEnter(DragContext context) { super.onEnter(context); cart.addStyleName("enterCart"); } public void onLeave(DragContext context) { super.onLeave(context); cart.removeStyleName("enterCart"); }
This controller accepts a GWT widget (the drop target) in its constructor and I had to implement the behavior for the following events: onDrop
, onEnter
, onLeave
, and onPreviewDrop
. The implementations of onEnter
, and onLeave
are simple. Here, I just add and remove a css class which can be used to give a visual indicator when the the book gets dragged over the cart. The onDrop
method is used to add the dragged book object to the shopping cart and updating its display. I don’t use onPreviewDrop
here, but it can be used to cancel a drop if some requirement isn’t met by throwing a VetoDragException
.
Finally, to tie it all together, we need to register the drag controller with the drop controller like so:
()
ShoppingCart cart = new ShoppingCart(); CartDropController dropController = new CartDropController(cart); dragController.registerDropController(dropController);
This is the demo in action. Hopefully this will get you started with using the gwt-dnd library. The project page has several demos to show what the library is capable of. I’ve only just touched the surface of what can be done, and there’s a lot of functionality out of the box that you can play with. So take a look around!