Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



Drag & Drop

Compatible Flex. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par slyc3 (Steve Domin), le 15 juillet 2008

Bonjour,

Dans ce tutorial je vais vous expliquer comment utiliser les méthodes de Drag&Drop sous Flex.
En effet, bien que la mise en œuvre soit très simple si on s’en tient aux composants gérant nativement le Drag&Drop, elle peut s’avérer un peu plus compliqué si l’ont veut le mettre en place avec d’autres éléments.
Afin de répondre spécifiquement à chaque problème j’ai divisé mon tutoriel en fonction des cas :

  1. Drag&Drop entre composants le gérant nativement
  2. Drag&Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement
  3. Drag&Drop entre deux composants ne le gérant pas nativement



1)Drag&Drop entre composants le gérant nativement

Voici ces composants :

  • Datagrid
  • List
  • HorizontalList
  • PrintDatagGrid
  • TileList
  • Tree

Pour commencer nous allons créer dans notre une application deux <Datagrid> avec deux colonnes. Remplissez la liste avec trois <Object> contenant ce que vous désirez. Ces objets devront avoir deux propriétés et vous devrez indiquer à vos deux datagrid à laquelles de ces propriétés chacunes de leurs colonnes se réfèrent.
Vous devriez obtenir quelquechose comme ceci :
1.jpg

Et le code associé :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:Text x="151" y="29" width="508" height="22" fontSize="13"
text="1) Drag&amp;Drop entre deux composants le gérant nativement" />
 
	<mx:DataGrid id="datagridEnvoyeur" x="151" y="86" width="204" height="142">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
		<mx:dataProvider>
			<mx:Object objet="cd" prix="10"/>
			<mx:Object objet="bd" prix="7"/>
			<mx:Object objet="parfum" prix="30"/>
		</mx:dataProvider>
	</mx:DataGrid>
 
	<mx:DataGrid id="datagridReceveur" x="477" y="86">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
	</mx:DataGrid>
</mx:Application>


Notre objectif est de pouvoir ajouter dans le <Datagrid> vide les éléments de notre premier <Datagrid>, par simple Drag&Drop. Pour ceci nous devons autoriser le Drag depuis le <Datagrid> contenant les <Objects> et autoriser le Drop dans notre deuxième <Datagrid>. Il faut pour cela passer les propriétés dragEnabled et dropEnabled à « true ».

Ce qui devrait donner :

<mx:DataGrid id="datagridEnvoyeur" x="151" y="86" width="204" height="142" dragEnabled="true">

Et

<mx:DataGrid id="datagridReceveur" x="477" y="86" dropEnabled="true">

Et voilà!! Nous pouvons maintenant réaliser notre première opération de Drag&Drop sous Flex.
Cependant on peut remarquer quelques défauts, ou tout du moins quelques améliorations possibles, à notre application.
Par exemple on peut imaginer qu’une fois un élément déplacé d’un <Datagrid > il disparaisse de ce dernier.
Ceci peut-être réalisé très simplement en passant le propriété drapMoveEnabled à « true ».

<mx:DataGrid id="datagridEnvoyeur" x="151" y="86" 
width="204" height="142" dragEnabled="true" dragMoveEnabled="true">

Une autre manière d’améliorer notre petite application serait de faire en sorte que l’on puisse mettre un élément du <Datagrid> receveur dans le <Datagrid> envoyeur, là aussi par simple Drag&Drop. Et c’est une fois de plus très facile puisqu’il suffit de faire passer la propriété dragEnabled du <Datagrid> receveur à « true » et la propriété dropEnabled du <Datagrid> envoyeur à « true » elle aussi.
N’oubliez pas de faire passer la propriété dragMoveEnabled du <Datagrid> receveur à « true » si vous voulez que chaque élément que vous faites passer d’un <Datagrid> à un autre disparaisse du <Datagrid> d’où il vient.

Voilà ce que vous devriez avoir après tous ces menus changements:

<mx:DataGrid id="datagridEnvoyeur" x="151" y="86" dropEnabled="true"
width="204" height="142" dragEnabled="true" dragMoveEnabled="true" >

Et

<mx:DataGrid id="datagridReceveur" x="477" y="86" 
dropEnabled="true" dragEnabled="true" dragMoveEnabled="true">

Enfin sachez qu’il est possible de sélectionner et déplacer plusieurs éléments d’une même liste grâce à la propriété allowMultipleSelection. Si vous faîtes passer cette dernière à « true » sur vos deux <Datagrid> vous pourrez alors sélectionnez plusieurs éléments et les déplacer vers l’autre <Datagrid>. La sélection de plusieurs éléments se fait grâce à la touche « Ctrl ».

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:Text x="151" y="29" width="508" height="22" fontSize="13"
text="1) Drag&amp;Drop entre deux composants le gérant nativement" />
 
	<mx:DataGrid id="datagridEnvoyeur" x="151" y="86" width="204"
 height="142" dragEnabled="true" allowMultipleSelection="true" dragMoveEnabled="true">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
		<mx:dataProvider>
			<mx:Object objet="cd" prix="10"/>
			<mx:Object objet="bd" prix="7"/>
			<mx:Object objet="parfum" prix="30"/>
		</mx:dataProvider>
	</mx:DataGrid>
 
	<mx:DataGrid id="datagridReceveur" x="477" y="86" dragEnabled="true"
 dragMoveEnabled="true" dropEnabled="true" allowMultipleSelection="true">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
	</mx:DataGrid>
</mx:Application>

Télécharger le projet source.

2) Drag&Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement

Maintenant que vous êtes au point sur les techniques de Drag&Drop natives nous allons un peu compliquer le tout et réaliser une opération de Drag&Drop entre un composant le gérant nativement, dans notre exemple un <Datagrid>, et un autre ne le gérant pas, à savoir une image.
Nous voulons pouvoir déplacer les éléments d’un tableau vers une image de poubelle indiquant donc la destruction de ces éléments du tableau, à la manière d’un client d’un site de VPC voulant supprimer des articles de son panier.

Nous devons créer un <Datagrid>, qui contiendra nos objets achetés, et ajouter un composant <Image> contenant une image de corbeille(disponible en fichier joint).
Voici ce que vous devriez obtenir :
2.jpg
Et le code associé :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:ArrayCollection id="listeObjets">
		<mx:Object objet="cd" prix="10"/>
		<mx:Object objet="bd" prix="7"/>
		<mx:Object objet="parfum" prix="30"/>
	</mx:ArrayCollection>
 
	<mx:Text x="181" y="28" width="548" height="29" fontSize="13"
text="2) Drag&amp;Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement" 
/>	
 
	<mx:DataGrid id="tableauMembres" dataProvider="{listeObjets}" x="192.5" y="95" width="606">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
	</mx:DataGrid>
 
	<mx:Image id="imageCorbeille" source="corbeille.png" height="88" x="409" y="287" width="92"/>
 
</mx:Application>

Notre objectif maintenant est d’autoriser le Drag&Drop depuis le <Datagrid>. Pour cela nous allons réutiliser la technique vu dans la première partie du tutorial qui consiste simplement à passer la propriétés dragEnabled de ce <Datagrid> à « true ». Tant que nous y sommes profitons en pour passer la propriété allowMultipleSelection à « true » elle aussi, ce qui permettra à l’utilisateur de supprimer plusieurs objets d’un coup.
Vous obtenez :

<mx:DataGrid id="tableauMembres" dragEnabled="true" allowMultipleSelection="true" 
dataProvider="{listeObjets}" x="192.5" y="95" width="525">

Ceci fait nous devons maintenant nous concentrer sur le Drop sur notre image de corbeille. Comme vous l’aurez constaté, le composant <Image> ne dispose pas de la propriété dropEnabled. Nous devons donc recourir à une autre technique qui consiste à créer de nouvelles fonctions pour les évènements dragEnter et dragDrop, et optionnelement les évènements dragOver et dragExit.
Ces évènements sont à définir sur la cible de votre opération de Drag (notre corbeille) .

Avant de continuer je vais me permettre de faire un petit topo sur ces évènements. Cela tiendra d’ailleurs plus de la traduction et de l’interprétation de la doc d’Abode sur le sujet.

  • dragEnter* : évènement diffusé lorsque vous déplacer le proxy (« l’image » représentant ce que vousêtes en train de « dragger ») au-dessus de votre cible de Drop (notre corbeille). La méthode doit permettre de vérifier le format des données qui sont en train d’être « dragger ». Une fois ceci fait vous devez appeler la méthode suivante : DragManager.acceptDragDrop() pour autoriser le drop sur votre cible de Drop.
  • dragOver : évènement diffusé lorsque vous déplacer la souris au dessus de votre cible de Drop et que vous avez accepté le Drop. Cela vous permettra d’ajouter diverses opérations, réalisées avant que les données ne soient « lâchées » sur la cible de Drop.
  • dragExit : événement diffusé si vous déplacez le proxy en dehors du dessus de votre cible de Drop. Si jamais vous aviez effectué des modifications de l’apparence lors de l’évènement dragEnter, vous pouvez réinitialiser l’apparence des composants modifiés grâce à cette évènement.
  • dragDrop* : évènement diffusé lorsque vous relâchez le bouton de la souris sur la cible de Drop. Cela « confirme » en quelque sorte l’opération et vous permez d’ajouter les données à la cible de Drop.


Voilà pour les informations à propos de ces évènements, si jamais vous voulez en savoir plus je vous conseille la partie de la documentation d’Adobe sur ce sujet.
Revenons à notre image de corbeille.

Nous devons donc créer une méthode pour l’évènement dragEnter. Nous irons au plus simple, c'est-à-dire sans vérifier les données qui sont « draggé » au dessus de notre corbeille.
Voici le code de cette méthode :

import mx.events.DragEvent;
import mx.managers.DragManager;
 
private function dragEnterHandler(event:DragEvent):void{
        var dropTarget:Image = event.currentTarget as Image;
	DragManager.acceptDragDrop(dropTarget);
	DragManager.showFeedback(DragManager.MOVE);
}

Dans un premier temps nous créons une variable représentant notre cible de Drop. Nous spécifions ensuite au DragManager d’accepter l’opération de Drag&Drop pour tout proxy qui serait placé au dessus de notre cible de Drop.
Nous lui demandons aussi d’afficher un feedback nous montrant que le drop est accepté.
Nous allons maintenant nous occupé de créer une méthode pour l’événement dragDrop. Celle-ci supprimera l’objet « draggé » du <Datagrid> .
/!\ Ce n'est pas la meilleure méthode du tout pour supprimer les éléments du tableau une fois dropper sur la corbeille, en effet passer la propriété dragMoveEnabled du <Datagrid> à “true” aurait été tout aussi efficace et beaucoup plus rapide. J'ai simplement créé cette méthode pour illustrer mon exemple par la pratique.

En voici le code :

private function dragDropHandler(event:DragEvent):void{
     var itemsArray:Array = event.dragSource.dataForFormat("items") as Array;    
     var longueur:uint = itemsArray.length;
     for(var i:uint = 0; i<longueur; i++){
         listeObjets.removeItemAt(listeObjets.getItemIndex(itemsArray[i]));
     }
}

Tout dabord nous créons un Array qui contiendra tous les objets que nous avons sélectionné et « draggé » au dessus de notre corbeille. Nous créons ensuite une variable qui contiendra la valeur de la longueur de notre Array.
Grâce à une boucle for nous retirons les éléments contenu dans cet Array de l’ArrayCollection listeObjets.

Voilà, c’est terminé pour cette partie, vous savez maintenant comment gérer le Drag&Drop si jamais vous utilisez une composant qui ne gère pas le drop nativement.

On peut tout de même suggérer quelques améliorations à notre mini application. Par exemple on pourrait remplacer le proxy de notre ligne de tableau par une image un peu plus parlante. Nous verrons justement comment spécifier une image pour le proxy dans la partie suivante.

Code final:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:Script>
		<![CDATA[
			import mx.events.DragEvent;
			import mx.managers.DragManager;
 
			private function dragEnterHandler(event:DragEvent):void{
				var dropTarget:Image = event.currentTarget as Image;
				DragManager.acceptDragDrop(dropTarget);
				DragManager.showFeedback(DragManager.MOVE);
			}
 
			private function dragDropHandler(event:DragEvent):void{
				var itemsArray:Array = event.dragSource.dataForFormat("items") as Array;	
			 	var longueur:uint = itemsArray.length;
			 	for(var i:uint = 0; i<longueur; i++){
			 		listeObjets.removeItemAt(listeObjets.getItemIndex(itemsArray[i]));
			 	}
			}
		]]>
	</mx:Script>
 
	<mx:ArrayCollection id="listeObjets">
		<mx:Object objet="cd" prix="10"/>
		<mx:Object objet="bd" prix="7"/>
		<mx:Object objet="parfum" prix="30"/>
	</mx:ArrayCollection>
 
	<mx:Text x="181" y="28" width="548" height="29" fontSize="13"
text="2) Drag&amp;Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement" />	
 
	<mx:DataGrid id="tableauMembres" dragEnabled="true" allowMultipleSelection="true" 
dataProvider="{listeObjets}" x="192.5" y="95" width="525">
		<mx:columns>
			<mx:DataGridColumn headerText="Objet" dataField="objet"/>
			<mx:DataGridColumn headerText="Prix" dataField="prix"/>
		</mx:columns>
	</mx:DataGrid>
 
	<mx:Image id="imageCorbeille" source="corbeille.png"
 dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)" height="88" x="409" y="287" width="92"/>
 
</mx:Application>

Télécharger le projet source.



3) Drag&Drop entre deux composants ne le gérant pas nativement

L’objectif dans cette 3ème et dernière partie et de permettre le Drag&Drop entre deux composants ne gérant ni le drag ni le drop nativement.
Dans la partie précédente nous avons vu comment autoriser le Drag&Drop depuis un <Datagrid> vers une image. Nous allons maintenant voir comment « dragger » une image vers une autre image.
Pour cela nous allons donc reprendre le code mxml de la partie précédente mais en remplaçant le <Datagrid> par une pochette de cd de votre choix.

En voici un exemple :
3.jpg
Et le code que vous devriez obtenir :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:Script>
		<![CDATA[
			import mx.events.DragEvent;
			import mx.managers.DragManager;
 
			private function dragEnterHandler(event:DragEvent):void{
				var dropTarget:Image = event.currentTarget as Image;
				DragManager.acceptDragDrop(dropTarget);
				DragManager.showFeedback(DragManager.MOVE);
			}
 
			private function dragDropHandler(event:DragEvent):void{
 
			}
		]]>
	</mx:Script>
 
	<mx:Text x="181" y="28" width="606" height="29" fontSize="13"
 text="3) Drag&amp;Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement"/>	
 
	<mx:Image x="372" y="78" source="{cover}" width="166" height="166"
 id="coverImage"/>
 
	<mx:Image id="imageCorbeille" source="corbeille.png"
 dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)"
 height="88" x="409" y="287" width="92"/>
 
</mx:Application>

Nous devons créer une méthode sur l’évènement mouseDown de notre pochette cd dont voici le code :

private function mouseDownHandler(event:MouseEvent):void 
    var dragInitiator:Image = event.currentTarget as Image;
 
    var dragSource:DragSource = new DragSource();
 
    dragSource.addData(dragInitiator, "img");
 
    var proxy:Image = new Image();
    proxy.source = "cover.jpg";
    proxy.width = 166;
    proxy.height = 166;
 
    DragManager.doDrag(dragInitiator, dragSource, event,proxy);
}

Nous créons tout d’abord une variable dragIniator qui contient la référence vers l’objet qui a initié le Drag, dans notre cas la pochette cd.
Ensuite nous créons une nouvelle instance d’un objet DragSource qui sera contenu dans notre variable dragSource.Cette DragSource contient les données qui sont « draggés ».
Nous ajoutons ensuite un format à cette DragSource qui nous servira lorsque nous voudrons vérifier le format de donnée pour savoir si on accepte le drop ou non.
Je vous en parlais tout à l’heure, il est possible de spécifier ce que l’on veut afficher comme proxy lors du drag. On peut spécifier de nombreux paramètres à ce proxy, notamment la hauteur et la largeur qui sont obligatoire si l’on veut que notre proxy s’affiche.
Pour finir il suffit de faire appel à la méthode doDrag() du DragManager pour valider le Drag.
On voit que cette méthode prend plusieurs paramètre : dragInitiator, dragSource, mouseEvent, soit l’évènement qui contient la position de la souris lors du début du Drag et dragImage qui contient en fait la référence vers notre proxy. On peut aussi spécifier l’offset en x et y pour notre proxy, l’alpha de ce dernier et si l’on veut « copier ou couper » l’objet que l’on drag.(De la même manière que l’on passerait la propriété dragMoveEnabled à « true » avec un composant gérant le Drag&Drop en natif).
Les trois premiers (dragInitiator, dragSource et mouseEvent) sont obligatoires si vous voulez que votre Drag&Drop fonctionne.

La dernière petite amélioration qu’il nous reste à apporter est une modification de la méthode dragEnterHandler(). En effet on veut désormais que cette dernière vérifie le format des données « draggé » avant d’accepter le Drag&Drop.
Voici le code de la fonction transformé :

private function dragEnterHandler(event:DragEvent):void{
     var dropTarget:Image = event.currentTarget as Image;
     if (event.dragSource.hasFormat("img")){
          DragManager.acceptDragDrop(dropTarget);
     }
     DragManager.showFeedback(DragManager.MOVE);
}


Enfin, pour ce qui est de la fonction dragDropHandler() on affiche juste une Alert nous indiquant que le Drag&Drop à réussi.

Voici le code final de cette troisième partie :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
 
	<mx:Script>
		<![CDATA[
			import mx.core.DragSource;
			import mx.events.DragEvent;
			import mx.managers.DragManager;
			import mx.controls.Alert;
 
 
			private function mouseDownHandler(event:MouseEvent):void {
 
	            var dragInitiator:Image = event.currentTarget as Image;
 
	            var ds:DragSource = new DragSource();
				ds.addData(dragInitiator, "img");
 
				var proxy:Image = new Image();
				proxy.source = "cover.jpg";
				proxy.width = 166;
				proxy.height = 166;
	            DragManager.doDrag(dragInitiator, ds, event,proxy);
	        }
 
 
			private function dragEnterHandler(event:DragEvent):void{
				var dropTarget:Image = event.currentTarget as Image;
				if (event.dragSource.hasFormat("img")){
					DragManager.acceptDragDrop(dropTarget);
				}
				DragManager.showFeedback(DragManager.MOVE);
			}
 
			private function dragDropHandler(event:DragEvent):void{
				Alert.show("Votre drag&drop a réussi");
			}
 
 
		]]>
	</mx:Script>
 
 
 
	<mx:Text x="181" y="28" width="606" height="29" fontSize="13"
 text="3) Drag&amp;Drop entre un composant gérant le drag nativement et un ne gérant pas le drop nativement"/>	
 
	<mx:Image x="372" y="78" source="cover.jpg" width="166" height="166"
 id="coverImage" mouseDown="mouseDownHandler(event)"/>
 
	<mx:Image id="imageCorbeille" source="corbeille.png"
 dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)"  
height="88" x="409" y="287" width="92"/>
 
 
</mx:Application>

Télécharger le projet source.




Et voilà, c’est terminé !! J’espère vous avoir un peu éclairé sur les différentes façons de réaliser des opérations de Drag&Drop sous Flex que ce soit en utilisant les méthodes gérées nativement par les composants ou en utilisant de l’AS3.
Si vous souhaitez en savoir plus, vous pouvez venir en discuter ici sur le forum, ou encore je vous invite à m’écrire à :
slyc3isback[at]me.com
Ou alors à consulter la documentation d’Adobe qui est vraiment très complète à ce sujet.



Télécharger l'article au format pdf.