Because almost every graphical operating system environment supports some level of drag and drop functionality, AIR provides an easy way to work with drag and drop gestures across multiple platforms. AIR supports both the dragging out and the dropping in of the standard data types; however, ActionScript and JavaScript deal with these gestures in very different ways.
Use the NativeDragManager
class
to control the drag-in gesture in your AIR application.
In ActionScript, you manage drag and drop gestures with the
NativeDragManager
class. To support a
drag-out gesture specifically, you use the doDrag
static method of the NativeDragManager
class. You can pass in five
parameters; the first two are required: dragInitiator
and clipboard
.
The first required parameter, dragInitiator
, is the object that initiated
the drag action. It is required to be an InteractiveObject
or a class that inherits
from it. The second parameter, clipboard
, is an instance of the Clipboard
class or a class inheriting from the
Clipboard
class. In this case, you
actually need to instantiate an instance of the Clipboard
class and add the data that needs to
be passed with the drag-out gesture.
In this example, the user interface consists of a label, a button, and a list:
<mx:Label id="directoryName" fontSize="20" fontWeight="bold" /> <mx:Button label="Select Directory" click="{file.browseForDirectory('Select Directory')}" /> <mx:List id="fileList" width="100%" height="100%" dataProvider="{files}" labelField="name" mouseDown="handleMouseDown(event)" />
When the button is clicked, it prompts the user to select a directory. When the user selects a directory, the list will be populated with all the files in the directory. The code you need is as follows:
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/ 2006/mxml" layout="vertical" preinitialize="init();"> <mx:Script> <![CDATA[ import mx.core.UIComponent; import mx.collections.ArrayCollection; import flash.filesystem.File; [Bindable] private var files:ArrayCollection = new ArrayCollection(); private var file:File = new File(); private function init():void { file.addEventListener( Event.SELECT, handleDirectorySelect ); } private function handleDirectorySelect( event:Event ):void { files = new ArrayCollection( file.getDirectoryListing() ); directoryName.text = file.name; }
When the user performs a mouseDown
gesture on the list, the handleMouseDown
method is called. First, a new
instance of the Clipboard
class is
created. The instance of the File
class is passed into the instance of the Clipboard
with the setData
method. It is passed in as a
one-element array because the ClipboardFormats.FILE_LIST_FORMAT
is expecting
an array. Next, a new instance of the BitmapData
class is instantiated. This will
contain the image that will be dragged. For the example, the BitmapData
instance draws the list item that
is being dragged. Finally, the doDrag
method is called, and the list item, the instance of the Clipboard
class, and the instance of the
BitmapData
class are passed in as
parameters. The code for this is as follows:
private function handleMouseDown( event:MouseEvent ):void { if (fileList.selectedItem) { var data:Clipboard = new Clipboard(); data.setData( ClipboardFormats.FILE_LIST_FORMAT, [ fileList.selectedItem ] ); var bmd:BitmapData = new BitmapData( InteractiveObject( event.target ).width, InteractiveObject( event.target ).height ); bmd.draw( InteractiveObject( event.target ) ); NativeDragManager.doDrag( event.target as InteractiveObject, data, bmd ); } }
Just as with the clipboard, you can add multiple types of data to a draggable object, enabling the receiving application to select the type of data that is most relevant.
Listen to the dragstart
event
within the HTML in your AIR application.
In JavaScript, dragging and dropping is handled by listening for
specific events on an element. When dragging out, you listen
specifically for the dragstart
event.
In the method that is listening for the event, you define the actual
data that gets passed with the drag gesture.
To demonstrate the drag-out gesture in JavaScript, the following
example provides a modest user interface that consists of three
elements: a header div
, a button that
enables the user to select a directory, and a div
that contains a list of the files in that
directory.
<html> <head> <title>Entry 8.2 - JavaScript</title> <script type="text/javascript" src="AIRAliases.js"></script> <style type="text/css"> #files {width: 300px;height: 200px;overflow: auto;} #header{ font-weight: bold;font-size: 20px;} .listing {background-color: #efefef; padding: 3px;margin: 3px 0;-webkit- user-drag: element;} </style> </head> <body onload="init()" style="margin: 10px;"> <div id="header">Select a Directory</div> <input type="button" value="Select Directory" onclick="handleSelectButton()"/> <div id="files"></div> </body> </html>
Before the individual files can be dragged out of the application,
the previously mentioned items need to be listed in the div
named files
. When clicked, the Select Directory button launches the
directory selection window, and when a directory is selected, the
directorySelected
method is called.
This clears the files div
and adds a
new div
for each file. The important
item to notice here is that an event listener is being configured for
each div
that is listening for the
dragstart
event. This is the event
that is dispatched when the user performs a mouseDown
gesture on the object.
In addition, each new div
that
is created is assigned a property called fileReference
. The actual instance of the
File
class for each file is assigned
to this property. This assignment enables the instance to be passed with
the object when dragged.
var file = new air.File(); var directoryFiles = {}; function init() { file.addEventListener(air.Event.SELECT, directorySelected); } function handleSelectButton() { file.browseForDirectory("Select a directory"); } function directorySelected(event) { directoryFiles = file.getDirectoryListing(); var header = document.getElementById( 'header' ); header.innerHTML = file.name; var files = document.getElementById( 'files' ); while( files.hasChildNodes() ) { files.removeChild( listing.firstChild ); } for (i = 0; i < directoryFiles.length; i++) { var item = document.createElement( 'div' ); item.innerHTML = directoryFiles[i].name; item.className = "listing"; item.fileReference = directoryFiles[i]; item.addEventListener( "dragstart", handleDragStart ); files.appendChild( item ); } }
After each file div
is
configured to listen for the dragstart
event, you can add data to the drag
object. For this example, the data is copied, as you can see by the
String
value copy
assigned to the effectAllowed
property of the dataTransfer
object in the event. Next, the
data is added to the drag object with the setData
method just as with the clipboard. The
format and the actual data are both passed in.
Because the data format is air.ClipboardFormats.FILE_LIST_FORMAT
, the
data needs to be in an array variable. It will have only a single
element, which is the fileReference
property of the dragged object that was assigned earlier.
function handleDragStart(event) { event.dataTransfer.effectAllowed = "copy"; event.dataTransfer.setData(air.ClipboardFormats.FILE_LIST_FORMAT,new Array( event.target.fileReference ) ); }
With just these lines of code, the application now supports the drag-out gesture. In this case, if one of the file listings is dragged from the application to the desktop, a new copy of the file will appear on the desktop.
Working with other types of clipboard data is similar. You can add
multiple types of data to the dataTransfer
object, and an application that
has been written to receive dragged-in content can be configured to
handle the different data formats.
AIR provides the capability to support the drag-in gesture in both ActionScript and JavaScript.
In ActionScript, the NativeDragManager
class handles the drag-in
gesture. Its static method acceptDragDrop
enables you to define whether
you will accept the dragged data based on its contents.
In this example, the user interface consists of a box that functions as the target area and a text area that will be used to display the text data from the drag-in gesture:
<mx:WindowedApplication xmlns="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init()"> <mx:Script> ... </mx:Script> <mx:Box id="target" width="200" height="200" backgroundColor="#efefef" nativeDragEnter="handleDragEnter(event)" nativeDragDrop="handleDrop(event)" horizontalAlign="center" verticalAlign="middle"> <mx:Label text="Target Area" fontSize="20" fontWeight="bold" color="#666666" /> </mx:Box> <mx:TextArea id="content" width="100%" height="100%"/> </mx:WindowedApplication>
The target area box contains event listeners configured for the
nativeDragEnter
and nativeDragDrop
events. The nativeDragEnter
event is dispatched when
dragged data is brought over an object. If you plan to accept only
certain types of dropped data, you need to add logic to this method to
check the data formats. The NativeDragEvent
contains a property named
clipboard
that is an instance of the
Clipboard
class and that contains all
the data for the dragged object. You can use the hasFormat
method of this property to determine
whether the needed data is present. Once you are sure that the needed
data is present, you can call the NativeDragManager.acceptDragDrop
method and
pass in the target area as the parameter.
private function handleDragEnter( event:NativeDragEvent ):void { if( event.clipboard.hasFormat( ClipboardFormats.TEXT_FORMAT ) ) { NativeDragManager.acceptDragDrop( target ); } }
If the acceptDragDrop
method is
called, then the nativeDragDrop
method is dispatched when the object is dropped onto the target area. In
this case, once the data is dropped, any data in the ClipboardFormats.TEXT_FORMAT
format is
extracted and placed into the text area.
private function handleDrop( event:NativeDragEvent ):void { content.text = event.clipboard.getData( ClipboardFormats.TEXT_FORMAT ) as String; }
Listen to the dragenter
,
dragover
, and drop
events within the HTML of your AIR
application.
In JavaScript, the drag-in gesture is supported by listening for
specific events. If you want to use a div
as a target for the drag in, you need to
listen for three specific events: dragenter
, dragover
, and drop
. By default, you cannot use a noneditable
region as a drop target, but if you listen for the dragenter
and dragover
events and call the event.preventDefault
method, you can use
noneditable regions as drop targets.
To perform the drop, you need to listen for the drop
event. The drop
event contains a dataTransfer
object that works like the
Clipboard
class. You can call the
event.dataTransfer.getData
method and
pass in the data type. It then returns the data from the drag-in gesture
in that format.
In this example, the user interface consists of two div
elements. The first div
with an id
of targetArea
is used as the drop target. The
second div
with an id
of content
is updated with the value of the
dropped text.
<html> <head> <title>Entry 8.4 - JavaScript</title> <style type="text/css"> #targetArea { width: 250px;height: 250px;background-color: #efefef;border: 1px solid #cccccc; color: #cccccc;text-align: center;font-size: 20px; } #content { font-size: 11px; } </style> </head> <body onload="init()" style="margin: 10px;"> <div id="targetArea">Drop Target</div> <div id="content"></div> </body> </html>
The event listeners that are needed for the drag-in gesture are
added in the init
method, which is
called in response to the onload
event. Also, the method handleDefaultEvents
calls the event.preventDefault
method to allow the
targetArea
to be used as a drop
target.
function init() { var targetArea = document.getElementById( 'targetArea' ); targetArea.addEventListener( "dragenter", handleDefaultEvents ); targetArea.addEventListener( "dragover", handleDefaultEvents ); targetArea.addEventListener( "drop", handleDrop ); } function handleDefaultEvents( event ) { event.preventDefault(); }
The handleDrop
method responds
to the drop event. In this method, the text data is extracted from the
event by calling the event.dataTransfer.getData
method and passing
in the air.ClipboardFormats.TEXT_FORMAT
value. This
text is then added to the div
named
content
.
function handleDrop( event ) { var dragText = event.dataTransfer.getData( air.ClipboardFormats.TEXT_FORMAT ); var content = document.getElementById( 'content' ); content.innerHTML = dragText; }
You need to enable drag and drop gestures that work both within your application and outside your application.
Use the NativeDragManager
’s
static methods to enable both drag-in and drag-out gestures.
In ActionScript, you can use both static methods of the NativeDragManager
class to achieve drag and
drop functionality within your application. In this example, the user
interface consists of two VBox
components that function as drop targets. Each VBox
contains three labels that function as
the drag objects. Each Label
is
configured with an event listener that listens for the mouseDown
event. Each VBox
is configured with an event handler for
the nativeDragEnter
and nativeDragDrop
events.
<mx:Style> .target { backgroundColor: #ffffff; borderStyle: solid; borderColor: #666666; } </mx:Style> <mx:Label text="Drag and Drop" fontSize="20" fontWeight="bold" /> <mx:HBox> <mx:VBox id="target1" width="250" nativeDragEnter="handleDragEnter(event)" nativeDragDrop="handleDrop(event)" styleName="target"> <mx:Label text="One" mouseDown="handleMouseDown(event)" /> <mx:Label text="Two" mouseDown="handleMouseDown(event)" /> <mx:Label text="Three" mouseDown="handleMouseDown(event)" /> </mx:VBox> <mx:VBox id="target2" width="250" nativeDragEnter="handleDragEnter(event)" nativeDragDrop="handleDrop(event)" styleName="target"> <mx:Label text="Four" mouseDown="handleMouseDown(event)" /> <mx:Label text="Five" mouseDown="handleMouseDown(event)" /> <mx:Label text="Six" mouseDown="handleMouseDown(event)" /> </mx:VBox> </mx:HBox>
The handleMouseDown
method performs three specific functions:
It creates a new instance of the Clipboard
class and adds the text
property of the Label
as text data.
An instance of the BitmapData
class draws the Label
.
Calling the acceptDragDrop
method of the NativeDragManager
and passing it the Label
, the
instance of the Clipboard
class,
and the instance of the BitmapData
class initiates the drag
gesture.
The code you need is as follows:
private function handleMouseDown( event:MouseEvent ):void { var data:Clipboard = new Clipboard(); var labelToDrag:Label = Label(event.currentTarget); data.setData( ClipboardFormats.TEXT_FORMAT, labelToDrag.text ); var bmpProxy:BitmapData = new BitmapData(labelToDrag.width, labelToDrag.height ); bmd.draw(labelToDrag); NativeDragManager.doDrag(labelToDrag, data, bmpProxy); }
The handleDragEnter
method that
is triggered by the VBox
components
performs one specific function: accepting the drag-in gesture. The
static NativeDragManager.acceptDragDrop
method must
be called so the drag object knows the target is willing to accept its
data. To ensure this text is plain-text data, the hasFormat
method of the Clipboard
class is called.
private function handleDragEnter( event:NativeDragEvent ):void { if( event.clipboard.hasFormat( ClipboardFormats.TEXT_FORMAT ) ) NativeDragManager.acceptDragDrop(VBox(event.currentTarget)); }
The handleDrop
method takes the
data that was dropped and creates a new Label
in the target VBox
identical to the drag object:
private function handleDrop( event:NativeDragEvent ):void { var newLabel:Label = new Label(); newLabel.text = event.clipboard.getData( ClipboardFormats.TEXT_FORMAT ) as String; newLabel.addEventListener( MouseEvent.MOUSE_DOWN, handleMouseDown ); VBox( event.target ).addChild( newLabel ); }
With this simple application, you now can drag the label to either
target, and the application will be replicated. In addition, you can
drag any of these drag objects outside the application to any
application that supports plain-text data. The VBox
drag targets will also accept text
dragged in from outside the AIR application.
You need to enable drag and drop gestures that work both within your application and outside your application.
Listen to the dragenter
,
dragover
, dragstart
, and drop
events from the HTML within your AIR
application.
In JavaScript, you can perform drag and drop operations by
listening to the dragenter
, dragover
, dragstart
, and drop
events. In this example, the user
interface consists of two div
elements that function as drop targets. Each div
contains three div
elements that function as drag objects.
The target div
elements have event
listeners for the dragover
, dragenter
, and drop
events. The drag objects have an event
listener for the dragstart
event. For
example:
<html> <head> <title>Entry 8.6 - JavaScript</title> <script type="text/javascript" src="AIRAliases.js"></script> <script type="text/javascript" src="AIRIntrospector.js"></script> <script type="text/javascript"> function init() { var target1 = document.getElementById( 'target1' ); target1.addEventListener( "dragenter", handleDefaultEvents ); target1.addEventListener( "dragover", handleDefaultEvents ); target1.addEventListener( "drop", handleDrop ); var target2 = document.getElementById( 'target2' ); target2.addEventListener( "dragenter", handleDefaultEvents ); target2.addEventListener( "dragover", handleDefaultEvents ); target2.addEventListener( "drop", handleDrop ); } </script> <style type="text/css"> .target { width: 150px;height: 250px;border: 1px solid #cccccc; color: #cccccc;text-align: center;font-size: 20px; margin: 5px; } #content { font-size: 11px; } #sourceArea { background-color: #efefef;-webkit-user-drag: element; } .listItem {background-color: #efefef;-webkit-user-drag: element; font- size: 11px;margin: 3px;padding:3px;} </style> </head> <body onload="init()" style="margin: 10px;"> <h1>Drag and Drop</h1> <div id="target1" class="target" style="float: left;"> <div class="listItem" ondragstart="handleDragStart(event)">One</div> <div class="listItem" ondragstart="handleDragStart(event)">Two</div> <div class="listItem" ondragstart="handleDragStart(event)">Three</div> </div> <div id="target2" class="target" style="float: left;"> <div class="listItem" ondragstart="handleDragStart(event)">Four</div> <div class="listItem" ondragstart="handleDragStart(event)">Five</div> <div class="listItem" ondragstart="handleDragStart(event)">Six</div> </div> </body> </html>
The handleDefaultEvents
method
that is triggered by the drop target div
elements calls the event.preventDefault
method to enable them to
function as drop targets:
function handleDefaultEvents( event ) { event.preventDefault(); }
The handleDragStart
method that
is triggered when you drag one of the drag objects calls dataTransfer.setData
, passing the innerHTML
property of the object to be dragged
as the drag data. This will be used to create a duplicate div
when the drag object is dropped onto a
drop target.
function handleDragStart( event ) { event.dataTransfer.setData( air.ClipboardFormats.TEXT_FORMAT, event.target.innerHTML ); }
The handleDrop
method creates a
new div
with the same innerHTML
as the drag object and adds it to
the drop target. An event listener is added for the dragstart
event so the new div
can function as a drag object as
well.
function handleDrop( event ) { var newDiv = document.createElement( 'div' ); newDiv.innerHTML = event.dataTransfer.getData( air.ClipboardFormats.TEXT_FORMAT ); newDiv.className = 'listItem'; newDiv.addEventListener( "dragstart", handleDragStart ); event.currentTarget.appendChild( newDiv ); }
By using AIR’s drag and drop support, you can drag objects within the application or outside the application to any application that supports plain text.