Le RIA sono applicazioni Web che possiedono le caratteristiche delle normali applicazioni desktop e la possibilità di trascinare gli oggetti da una parte all'altra dello schermo utilizzando il mouse è proprio una di queste caratteristiche. In questo articolo vediamo come implementare il drag and drop nelle nostre applicazioni Web.
Per realizzare questo tutorial utilizziamo il Flex Framework e Flash Builder. Flex infatti mette in campo tre componenti per ili drag and drop:
- Drag Initiator: l'operazione di drag and drop inizia quando l'utente seleziona con il mouse l'oggetto da spostare. L'oggetto selezionato rappresenta il drag initiator
- Drag Source: contiene informazioni riguardo il component che si sta spostando. Inoltre contiene il
DragProxy
che rappresenta l'immagine visualizzata durante il trascinamento - Drop Target: il drag and drop termina quando viene rilasciato il mouse, alla fine dello spostamento. Il componente all'interno del quale termina il trascinamento è chiamato
dropTarget
. Quest'ultimo effettua una serie di controlli sulDragSource
per garantire che il formato dei dati sia compatibile. Se il drop target stabilisce che il formato dei dati non è accettabile, annulla l'operazione di drop
Nell'esempio che realizzeremo l'oggetto da spostare sarà un component di tipo Image
, si può realizzare il drag and drop anche altri tipi di oggetto, ma è bene ricordare che solo i component che estendono la classe IUIComponent possono essere collegati (bind) agli event listener di drag e drop.
Drag and drop in un singolo contenitore
Per prima cosa apriamo Flex Builder (o Flash Builder) e spostiamo sullo stage un contenitore, nel nostro caso un Canvas
, all'interno del quale inseriamo un component di tipo Image
. È di fondamentale importanza definire un colore di sfondo per il Canvas
, altrimenti non sarà possibile il drop, poichè lo sfondo trasparente non è un adeguato drop target.
Dobbiamo, poi, stabilire le dimensioni del Canvas
e la proprietà source
del component Image
.
Quindi attiviamo i listener per gli eventi di drag e drop sul Canvas
e di mouse move sull'immagine. All'evento di mouseMove
sull'Image
associamo il un metodo onMouseMove
, così definito:
private function onMouseMove(evt:MouseEvent):void { var initiator:Image = Image(evt.currentTarget); var source:DragSource = new DragSource(); source.addData(initiator, 'source_img'); DragManager.doDrag(initiator, source, evt); }
In questo metodo vengono definiti il drag initiator e il drag source (creato a partire dal drag initiator) e poi viene invocato il metodo doDrag
della classe DragManager
che ha il compito di iniziare l'operazione di drag and drop.
Gli eventi di drag e drop sul canvas saranno intercettati dai metodi onDragEnter
e onDrop
che definiamo nel modo seguente:
private function onDragEnter(evt:DragEvent):void { if (evt.dragSource.hasFormat('source_img')) { DragManager.acceptDragDrop(Canvas(evt.currentTarget)); } } private function onDrop(evt:DragEvent):void { Image(evt.dragInitiator).x = Canvas(evt.currentTarget).mouseX; Image(evt.dragInitiator).y = Canvas(evt.currentTarget).mouseY; }
Il metodo onDragEnter
controlla il formato del drag source e definisce il drop target all'interno del Canvas
che abbiamo creato prima. L'evento dragEnter
occorre continuamente durante il drag e, quindi, il gestore onDragEnter
viene eseguito ripetutamente finchè non viene rilasciato il pulsante del mouse.
Il metodo onDrop
, invece, assegna al drag initiator le nuove coordinate che corrispondono a quelle del mouse al momento del drop event. Il codice completo dell'esempio è nel file allegato.
Definizione di un drag proxy
Se non esplicitamente specificato, Flex mostrerà il dragProxy di default. Il drag proxy può essere facilmente personalizzato modificando l'invocazione del metodo doDrag
.
Per specificare il proxy dobbiamo passare al metodo doDrag
alcuni parametri opzionali, nell'ordine:
Parametro | Descrizione |
---|---|
dragImage |
è l'immagine da visualizzare durante il dragging (deve essere un oggetto di tipo Image) |
xOffset |
indica l'offset sull'asse x rispetto all'angolo in alto a sinistra del drag initiator |
yOffset |
indica l'offset sull'asse y rispetto all'angolo in alto a sinistra del drag initiator |
alpha |
valore compreso tra 0 e 1 che indica il grado di opacità dell'immagine (0 è trasparente, 1 è opaco) |
xOffset
e yOffset
sono solitamente numeri negativi e servono per rispettare la posizione iniziale del mouse sull'oggetto trascinato, fino alla fine dello spostamento. Se alpha
non è specificato, viene utilizzato il valore di default pari a 0.5.
Modifichiamo il onMouseMove aggiungendo le linee di codice che ci servono a definire il drag proxy:
private function onMouseMove(evt:MouseEvent):void { var initiator:Image = Image(evt.currentTarget); var source:DragSource = new DragSource(); source.addData(initiator, 'source_img'); var proxy:Image = new Image(); proxy.source = imgProxy; proxy.width = 20; proxy.height = 21; DragManager.doDrag(initiator, source, evt, proxy, -10, -10, 0.8); }
Nota: è necessario stabilire le dimensioni dell'immagine del drag proxy, altrimenti non sarà visualizzata.
Drag and drop con componenti list-based
A differenza degli altri component, quelli basati su List
(List
, HorizontalList
, TileList
, Menu
, Tree
, DataGrid
, PrintDataGrid
) gestiscono nativamente le funzionalità di drag and drop. L'unica condizione per effettuare il drag and drop è che la struttura dei data provider dei component di partenza e destinazione sia la stessa.
Realizziamo ora un esempio con due component list-based. Per prima cosa trasciniamo due List
sullo stage e definiamone le dimensioni. Nel nostro esempio il dataProvider
della prima lista è definito manualmente al creationComplete
dell'applicazione.
Per definire il drag initiator è necessario impostare a true
la proprietà dragEnabled
, mentre per definire il dropTarget è necessario impostare a true
la proprietà dropEnabled
delle List
così come mostrato di seguito (file ListDragDrop.mxml).
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init();"> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; private function init():void { lista_partenza.dataProvider = new ArrayCollection([ {Nome:'Nicola', Cognome:'Strisciuglio', Eta:21}, {Nome:'Marco', Cognome:'Rossi', Eta:23}, {Nome:'Enzo', Cognome:'Ferrari', Eta:20}, {Nome:'Giulia', Cognome:'Roma', Eta:16} ]); } ]]> </mx:Script> <mx:HBox x="0" y="27" width="100%"> <mx:Panel width="200" height="270" layout="absolute" title="Lista 1"> <mx:List id="lista_partenza" x="0" y="0" width="180" height="230" labelField="Nome" dragEnabled="true" /> </mx:Panel> <mx:Panel width="200" height="270" layout="absolute" title="Lista 2"> <mx:List id="lista_dest" x="0" y="0" width="180" height="230" labelField="Nome" dropEnabled="true" /> </mx:Panel> </mx:HBox> </mx:Application>
A questo punto, il drag e drop dalla lista 1 alla lista 2 funziona correttamente, solo che al momento del drop l'oggetto viene copiato nella lista di destinazione. Per evitare la copia ed effettuare solo lo spostamento dell'oggetto è necessario impostare a true
la proprietà dragMoveEnabled
della lista di partenza, come mostrato di seguito. Una volta abilitato il dragMoveEnabled
si può comunque effettuare la copia dell'oggetto tenendo premuto il pulsante CTRL
(cmd
per gli utenti Mac) durante il trascinamento.
<mx:List id="lista_partenza" x="0" y="0" width="180" height="230"
labelField="Nome"
dragEnabled="true"
dragMoveEnabled="true" />
Talvolta, i dati possono subire delle modifiche durante il drag and drop. È questo il caso del drag and drop tra una List
e una DataGrid
. Osserviamo che non varia la struttura del dataProvider
ma solo i dati visualizzati.
Modifichiamo l'esempio precedente sostituendo la List
di destinazione con una DataGrid
, di cui impostiamo a true
la proprietà dropEnabled
. Progettiamo la DataGrid
in modo che siano visualizzate le tre colonne corrispondenti ai campi del DataProvider
della List
di partenza.
Osserviamo che all'operazione di drop nel datagrid verranno visualizzate tutte le informazioni riguardanti il singolo oggetto, cosa che non avveniva con la lista.
<mx:DataGrid id="dest_datagrid" x="0" y="0" width="280" height="230"
dropEnabled="true">
<mx:columns>
<mx:DataGridColumn headerText="Nome" dataField="Nome" />
<mx:DataGridColumn headerText="Cognome" dataField="Cognome" />
<mx:DataGridColumn headerText="Età" dataField="Eta" />
</mx:columns>
</mx:DataGrid>