Gestion du Drag & Drop complexe en Flex avec le DragManager
13 nov. 2008
Par Jean Marie Macé - Exemples Flex - Lien permanent
Dans cet article je vais vous présenter la classe DragManager et tout
ce qui tourne autour de la gestion complète du Drag & Drop en Flex.
En effet, certains composant du framework, comme la List par exemple
permet facilement avec très peu de code de gérer le drag & drop entre 2 composants(cf mon précédent article). Mais lorsqu'il s'agit de composant personnel ou bien de donnée plus complexe, comment faire?
Pour commencer, expliquons rapidement ce à quoi nous souhaitons arriver. Il sagit d'avoir un magasin dans lequel on vient piocher des éléments que l'on peut dragger(rien à voir avec les relations homme-femme!) sur des listes. Nous aurons donc 3 types d'objets différencier ici par 3 couleurs(pour faire simple) et donc 3 listes d'acceuil correspondant à chacune des couleurs.
Le principe du Drag & Drop est relativement simple si nous le décomposons en étape:
-click enfoncé sur un objet
-déplacement de la souris(c'est a ce moment que l'on parle de drag)
-click relaché sur la cible(c'est ce que l'on appel le drop)
Je vous épargne la mise en place des informations et des données sur mon application de démonstration, même si je vous invite à jeter un oeil(les débutants) sur la structure du projet, j'ai pris la peine de commenter et de montrer plusieurs aspects(Event personnalisé, Value Object, ...)
La première étape, consiste donc a détecter l'appui sur le bouton gauche de la souris sur un élément de notre magasin. Notre magasin étant une liste gérée par un dataProvider et un itemRenderer, c'est donc dans ce renderer(listRenderer.mxml) que le point de départ se situe. Voici le code présent dans le listRenderer, les commentaires sont assez clair je pense:
private function init():void
{
// on écoute l'appui sur le bouton gauche de la souris
this.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
// on écoute le relachement du bouton de la souris
this.addEventListener(MouseEvent.MOUSE_UP, _onMouseUp);
}
private function _onMouseDown(evt:MouseEvent):void
{
// on enregistre la point de départ
_dragDownPt = new Point(mouseX,mouseY);
// puis on écoute l'éventuel déplacement de la souris
this.addEventListener(MouseEvent.MOUSE_MOVE,_onMouseMoveDuringDrag);
}
private function _onMouseUp(evt:MouseEvent):void
{
// si relachement on n'écoute plus le déplacement de la souris
this.removeEventListener(MouseEvent.MOUSE_MOVE, _onMouseMoveDuringDrag);
}
private function _onMouseMoveDuringDrag(evt:MouseEvent):void
{
// astuce pour s'assurer que la souris a vraiment bouger volontairement
if(Math.abs(_dragDownPt.x - mouseX) <= 2 && Math.abs(_dragDownPt.y - mouseY) <= 2)
return;
// dispatch de l'event depuis le parent pour pouvoir écouter cet event dans l'application
this.parent.parent.dispatchEvent(new myDragEvent(myDragEvent.START_DRAG, data as imageVO, this, evt));
return;
}
Vous avez remarqué une petite astuce qui consiste a laisser une petite zone avant de considérer qu'il s'agit vraiment d'un mouvement volontaire de la part de l'utilisateur, il ne s'agit que d'une comparaison de la position actuel de la souris avec la position initiale enregistrée lors du click. En dernier lieu on dispacth un événement personnalisé(ici myDragEvent) depuis un parent afin de pouvoir l'écouter dans le reste de l'application(cas particulier d'un renderer).
Il s'agit maintenant d'écouter cet événement de lancement du drag pour agir en conséquence. Dans ma classe magasin j'écoute cet événement et dans son handler j'effectue ce traitement:
// on ajoute un filtre d'ombre(facultatif évidement)
var _dragFilter:DropShadowFilter = new DropShadowFilter();
// on prépare les données qui seront transportées
var ds:DragSource = new DragSource();
ds.addData(evt.imageItem,"item");
// on lance le drag via le DragManager en lui passant l'UIcomponent cible,les données et l'événement
// souris qui a déclenché le drag & Drop(mouse down puis mouse move). Les autres paramêtre de la méthode
// doDrag permettent notamment d'ajouter une image pour le rendu lors du déplacement de l'objet
DragManager.doDrag(evt.renderer,ds,evt.mouseEvent);
evt.renderer.filters = [_dragFilter];
La classe DragManager permet de gérer facilement le drag & drop, la méthode doDrag permet de gérer les éléments qui suivent le curseur souris, effet d'alpha sur l'élément draggé en lui passant en premier paramètre un objet de type UIComponent. Le second paramètre est un objet DragSource qui contient les données en transit. Le troisième est le MouseEvent qui a déclenché le drag. Le reste des paramètres sont facultatifs et permettent notament de gérer l'image lors du déplacement.
Voila, à présent nous n'avons plus qu'a gérer la partie drop. Il s'agit d'empecher le drop d'un objet sur une liste qui ne correspond pas à sa couleur. Pour cela nous écoutons l'événement DRAG_ENTER sur chacune des listes d'acceuil afin d'autoriser ou non le drop grace à la méthode acceptDragDrop. De plus , nous indiquons visuellement le type de drag & drop dont il s'agit avec la méthode showFeedbac, ici nous copions les éléments du magasin vers les listes, voici un extrait du code pour la liste rouge:
private function dragEnter(evt:DragEvent):void
{
// on vérifie que l'élément qui vient d'entrer en survol sur une liste correspond bien au format authorisé
// cf. la préparation des données l.39 de MagasinBase.as
if( evt.dragSource.hasFormat( "item" ) )
{
// on récupère puis cast les données pour les exploiter
var ivo:imageVO = evt.dragSource.dataForFormat("item") as imageVO;
switch(ivo.color)
{
case "red": // si c'est un élément rouge et que c'est la liste rouge qui a déclencher
// l'événement DRAG_ENTER alors on autorise le drop sur cette liste
// et on ajoute un retour visuel indiquant la copie de l'élément draggé
// il existe 4 types de feedback(cf. doc)
if(evt.currentTarget.toString().match("redList"))
{
DragManager.acceptDragDrop(this.redList);
DragManager.showFeedback( DragManager.COPY );
}
break;
}
}
}
Enfin, maintenant que chaque élément ne peut être droppé qu'au bon endroit il ne suffit plus que d'écouter l'événement DRAG_DROP qui indique le drop d'un objet sur un composant, puis d'ajouter cet objet dans la liste d'acceuil:
var ivo:imageVO = evt.dragSource.dataForFormat("item") as imageVO;
switch(ivo.color)
{
case "red": (this.redList.dataProvider as ArrayCollection).addItem(ivo);
break;
case "blue": (this.blueList.dataProvider as ArrayCollection).addItem(ivo);
break;
case "green": (this.greenList.dataProvider as ArrayCollection).addItem(ivo);
break;
default: break;
}
Enfin, voici la démo en action: ICI (les sources dispo: click droit)
Commentaires
merci pour ce topic, mais faut que les mentalites change!
mouislouisUn blog est un journal personnel en effet mais surtout un lieu dechange et de partage d idees (tout comme je fais actuellement sur le sujet) Bref, Merci pour les tuyaux, cest tres enrichissant.
papillondunord