Friday, July 13, 2007

Drag and Drop AIR Example

Adobe Integrated Runtime (AIR) offers developers some pretty cool features. Nevertheless, as we work towards version 1.0, there is still some catch up work being done to add in basic functionality for things like drag and drop, local file save, read, etc. One of the topics I wanted to write more about is the drag and drop functionality.

So what is drag and drop really? When you start to look at the architectural pattern, it is one of copying an original and showing a thumbnail of the original while in transit to being placed somewhere else in the environment. There are two types of drag – the copy drag and the move drag. In the copy drag, the original is left in place and the copy is placed in the target location. In the move drag, the original is removed. This requires a little more care as you have to be certain you do not destroy the original until you have confirmed the target is complete.

The first thing to do is to write a function that creates the bitmap of the item being dragged to produce a smooth effect while running. This function builds the object to drag and returns it:


public function
createTransferableData(image:Bitmap, sourceFile:File):TransferableData{

var transfer:TransferableData = new TransferableData();

transfer.addData(image,"Bitmap",false);
transfer.addData(image.bitmapData,"bitmap",false);
transfer.addData(new Array(sourceFile),"file list",false);

return transfer;
}

The TransferableData object has some unique properties in the new AIR package. This object will come in four basic formats. They are "text" (string data), "url" (URL string), "file list" (array of File objects) and "bitmap". The key method is addData(),which builds the new object. It is important to note that transferable data objects may have redundant information in multiple formats -- this increases the likelihood that recipient applications will understand the object.

The DragManager object handles most of the actual operations at runtime. The code is relatively simple to set up:



private function onMouseDown(event:MouseEvent):void{
var bitmap:Bitmap = Bitmap(event.target.content);
var bitmapFile:File = new
File(event.target.content.loaderInfo.url);
var transferObject:TransferableData = createTransferableData(bitmap,bitmapFile);



DragManager.doDrag(this,transferObject,bitmap.bitmapData,new
Point(-mouseX),(-mouseY));
}



Several things are happening with this code. First, the original copy is left in place when the drag operation starts. If your intent is merely to copy the object to the target, that might be okay. When moving, there are ways to make the original appear to go away but it is trickier than it seems because you have to take precautions to make sure the target gets dropped before destroying the original.

Another issue with this code as written is that by default, the copied image will appear offset to the original and this can be slightly ugly. The placement of the copied image can be tweaked by adding some fine tuning of the points as follows:


DragManager.doDrag(this,transferObject,bitmap.bitmapData,new
Point((-mouseX+65),(-mouseY+90)));



Now when you start the drag, the drag object (in the code below anyways) appears right where the original was regardless of where the mouse is placed.

If you want the original to simply disappear, you can do it easily by adding the following line of code into the onMouseDown() class:


bitmap.visible=false;


However, this removes both the original and the copy if the user releases the mouse button before the drag is complete. A better way hide the original is to set the visible property to false on the specific object, but the code is not as portable since each time it is used the object will have to be explicitly named.

The best way to really do this is with the NativeDragComplete event from the DragManager. A drag gesture involves the following event sequence:

nativeDragStart: A drag-and-drop gesture is begun by calling the DragManager.doDrag() method within a mouseDown or mouseMoved event handler. The DisplayObject passed to the doDrag() method becomes the initiating object and will dispatch a nativeDragStart event. (If the drag and drop starts outside of an AIR application, the rest of the sequence of events is the same, but there is no initiating object.)

nativeDragEnter, nativeDragOver: When a drag gesture passes over a DisplayObject, that object will dispatch a nativeDragEnter event. While the drag gesture remains over the DisplayObject, it will continually dispatch nativeDragOver events. In response to either of these events, an object that serves as a potential drop target should check the properties of the event object to decide whether it can accept the drop. If the data format and allowed actions are appropriate, then the event handler for these events must call DragManager.acceptDrop(), passing in a reference to the target object. The user will then be able to drop the dragged item onto the target.

nativeDragExit: When a drag gesture passes out of a DisplayObject, the object dispatches a nativeDragExit event. If the object earlier called the DragManager.acceptDrop() method, that call is no longer valid and acceptDrop() must be called again if the gesture re-enters the DisplayObject.

nativeDragDrop: When a DisplayObject has called DragManager.acceptDrop() and the user releases the drag gesture over the object, that object dispatches a nativeDragDrop event. The handler for that event can access the data in the transferable property of the event object and should set the DragManager.dropAction property to signal which action should be taken by the initiating object.

nativeDragComplete: When the user releases the mouse at the end of a drag gesture, the initiating object dispatches a nativeDragComplete event (whether or not the drop itself was consummated). The handler for this event can check the dropAction property of the event object to determine what, if any, modification should be made to its internal data state, such as removing a dragged-out item from a list. If dropAction==DragActions.NONE, then the dragged item was not dropped on an eligible target.


Here is a screenshot of me dropping an image of the Leela Palace Hotel into this document, minus the mouse pointer:





The code to play around with this is below:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import flash.desktop.DragManager;
import flash.desktop.TransferableData;
import flash.desktop.DragManager;
import flash.filesystem.File;
import flash.events.NativeDragEvent;

private function onMouseDown(event:MouseEvent):void{
var bitmap:Bitmap = Bitmap(event.target.content);
var bitmapFile:File = new
File(event.target.content.loaderInfo.url);
var transferObject:TransferableData =
createTransferableData(bitmap,bitmapFile);
DragManager.doDrag(this,transferObject,bitmap.bitmapData,new
Point((-mouseX+65),(-mouseY+90)));
bitmap.visible=false; //makes the original disappear
}

public function createTransferableData(image:Bitmap, sourceFile:File):TransferableData{

var transfer:TransferableData = new TransferableData();
transfer.addData(image,"Bitmap",false);
transfer.addData(image.bitmapData,"bitmap",false);
transfer.addData(new Array(sourceFile),"file list",false);
return transfer;
}
]]>
</mx:Script>
<mx:Panel x="10" y="10" width="310" height="361" layout="absolute" title="Panel 1">
<!--replace image with one from your own system-->
<mx:Image x="35" id="duaneImage" y="38" width="204" height="155"
mouseDown="onMouseDown(event)"
source="file:////Users/duane/Desktop/Picture 1.png"
mouseDownOutside="duaneImage.visible=(false)"/>
</mx:Panel>
</mx:WindowedApplication>


3 comments:

Do not spam this blog! Google and Yahoo DO NOT follow comment links for SEO. If you post an unrelated link advertising a company or service, you will be reported immediately for spam and your link deleted within 30 minutes. If you want to sponsor a post, please let us know by reaching out to duane dot nickull at gmail dot com.