Monday, August 10, 2009

what a drag

Like most developers, I make little shortcut utilities to help me bypass repetitive tasks. For some reason, though, I'd never bothered to come up with a solution for the tedious work of drag and drop. Nor have I researched anyone else's solution. But recently, I've had to make a lot of Sprites and MovieClips draggable. After the fifth or sixth time I'd added and removed a bunch of listeners, I thought "There's got to be an easier way."

I thought of two: the first was to extend Sprite, creating DraggableSprite. The second was to make a separate class that only handled dragging and leave Sprite's many other duties to Sprite itself. I settled on the latter, reasoning that Sprite was already bloated enough.

Before sharing my solution, let me outline the steps needed to make a Sprite draggable. First, you need a way to detect when the user has mouse-downed on the Sprite. So you have to add a listener for that.

Inside that listener's callback function, you can go head and call startDrag() on the Sprite. But you also need to remove the MOUSE_DOWN listener, because while the user is dragging the Sprite, listening for mouse downs is just burning computer cycles for nothing.

Meanwhile, you have add a listener for MOUSE_MOVE (in case you need to know when the Sprite moves, as you for the thumb control on a slider or scrollbar). You also need to add two MOUSE_UP listeners, one to the Sprite and one to the stage. The stage listener traps for the the user rolling off the Sprite before releasing his mouse button.

In the MOUSE_UP handler, you call stopDrag(), remove all of the above listeners, and re-add the MOUSE_DOWN listener, so that the process can start all over again.

These steps aren't too bad, unless you wind up having to do them over and over. Compare them with using my DragManager class, which you instantiate this way:

var dragManager : DragManager = new DragManger( mySprite );

That's it. mySprite is now dragabble. If you want control over the optional parameters of startDrag() -- lockCenter and bounds -- just pass them to the Dragmanager constructor as additional parameters:

var dragManager : DragManager = new DragManger( mySprite, true, myRect );

Note: lockCenter defaults to false; bounds defaults to null.

Of course, you usually want to do more than just make a Sprite or MovieClip draggable. You also want to know when it starts being dragged, where it moves, and when it stops being dragged. You can detect these things by adding listeners to the DragManager instance.


var dragManager : DragManager = new DragManger( mySprite );
draggManager.addEventListener( DragManager.START_DRAG, handler );
draggManager.addEventListener( DragManager.DRAG, handler );
draggManager.addEventListener( DragManager.STOP_DRAG, handler );

function handler( event : Event ) : void
{
trace( event.type, event.currentTarget.dragger.name );
}



If you run this code, you'll see the following output as you drag and drop your Sprite:

startDrag mySprite
drag mySprite
drag mySprite
drag mySprite
drag mySprite
drag mySprite
stopDrag mySprite

Note that, inside the handler, you can get access to the Sprite being dragged by referencing event.CurrentTarget.dragger.

DragManager has a couple of other tricks up its sleeve. You can disable dragging by calling disableDrag() and re-enable it by calling enableDrag(). You can also re-use the same DragManager by changing which object it controls:

var dragManager : DragManager = new DragManger( myMovieClip );

//later

dragManager.sprite = mySprite;

Note: ordinarily, I wouldn't bother being this thrifty. If I want to make both a Sprite and MovieClip draggable, I just do this:

var spriteDragManager : DragManager = new DragManger( mySprite );
var movieClipDragManager : DragManager = new DragManger( myMovieClip );

But the previous approach is useful if you want only one of the objects to be draggable. DragManagers can manage multiple objects, but only one at a time.

Other properties include lockCenter and bounds, which you can use if you want to change aspects of the drag after it's started. Note that if you do this, you need to disable and re-enable the drag to make the changes take effect:

var dragManager : DragManager = new DragManger( mySprite );

//later

dragManager.disableDrag();
dragManager.lockCenter = true;
dragManager.bounds = new Rectangle( 20, 100, 200, 0 );
dragManager.enableDrag();

Note: disableDrag() removes all listeners from the Sprite, so it's a good cleanup method for eliminating CPU-hogging processes.

It's such a joy to write classes like DragManager. Or, rather, it's a joy to HAVE written them. Once they're done and tested, you know you've cut a piece of tedium out of your life.

Here's the code: http://www.grumblebee.com/grumblecode/DragManager.zip

No comments:

Post a Comment