Découverte du framework Robotlegs [5/5]

La couche Controller

Les Events


Ici nous avons seulement un unique Event pour ce projet. C’est assez rare, le projet est vraiment petit.

package controller.events
{
	import model.vo.Entry;

	import flash.events.Event;

	public class DiaporamaEvent extends Event
	{
		// événements concernant l'application
		public static const STARTUP : String = 'startup';
		public static const CONFIG_COMPLETE : String = 'configComplete';
		public static const NEXT : String = 'next';
		public static const PREVIOUS : String = 'previous';

		// événements concernant la View
		public static const REFRESH : String = 'refresh';
		public static const LOAD_COMPLETE : String = 'loadComplete';		

		public var entry : Entry;

		public function DiaporamaEvent( type : String,
					    bubbles : Boolean = false,
					    cancelable : Boolean = false,
					    entry : Entry = null )
		{
			super(type, bubbles, cancelable);

			this.entry = entry;
		} 

		public override function clone():Event
		{
			return new DiaporamaEvent( type, bubbles, cancelable, entry );
		} 

		public override function toString():String
		{
			return formatToString("DiaporamaEvent", "type", "bubbles", "cancelable", "eventPhase");
		}

	}

}

On distingue 2 catégories d’Event.TYPE dans cette classe. On a vu plus haut que REFRESH ne concerne qu’un Mediator, c’est la même chose pour LOAD_COMPLETE. J’ai donc bien distingué ces deux catégories dans mes commentaires.

En effet, si tous les Event donnant lieu à une Command sont listés dans mon Context, les handlers présents dans les Mediators sont moins facilement repérables. Cela m’aidera à me relire que de le préciser dans ma classe DiaporamaEvent.

Les Commands

Les Commands contiennent la logique de l’application, ici nos seules fonctionnalités sont les clics sur suivant et précédent. On retrouve donc ces 2 Commands, prenons-en une sur les 2, elles sont très semblables :

package controller.commands
{
	import model.DiaporamaData;

	import org.robotlegs.mvcs.Command;

	public class NextCommand extends Command
	{
		[Inject]
		public var diaporamaData : DiaporamaData;

		override public function execute() : void
		{
			diaporamaData.next();
		}

	}

}

Lorsque l’événement DiaporamaEvent.NEXT survient, la Command est éxecutée, le Model est injecté, et on agit sur le Model par un appel de méthode publique. Voilà qui colle plutôt bien à la définition du Controller qu’on a donnée plus tôt, et à notre petit schéma.

Dans une Command, on agit sur le Model.

Si on relis DiaporamaContext, on a une StartupCommand, dispatchée au lancement de l’application :

package controller.commands
{
	import model.DiaporamaData;

	import org.robotlegs.mvcs.Command;

	public class StartupCommand extends Command
	{
		[Inject]
		public var diaporamaData : DiaporamaData;

		override public function execute() : void
		{
			diaporamaData.load();
		}

	}

}

Enfin on a une ConfigCompleteCommand, qui est appelée à la fin du chargement du XML. Pour l’instant, nous n’avons rien de visible sur la scène, notre appli est vide!

package controller.commands
{
	import controller.events.DiaporamaEvent;
	import view.components.DiaporamaDisplay;
	import view.components.DiaporamaControls;

	import org.robotlegs.mvcs.Command;

	/**
	 * appelée à la fin du chargement du .XML
	 */
	public class ConfigCompleteCommand extends Command
	{
		// on récupére l'Event déclencheur
		[Inject]
		public var event : DiaporamaEvent;

		override public function execute() : void
		{
			// on crée la vue
			contextView.addChild( new DiaporamaDisplay() );
			contextView.addChild( new DiaporamaControls() );

			// on force le refresh de la vue, pour afficher la première image
			dispatch( new DiaporamaEvent( DiaporamaEvent.REFRESH, false, false, event.entry ) );
		}

	}

}

C’est donc là qu’est créé la couche visible de notre application! On notera 2 choses très importantes sur cette Command :

  • l’injection d’un event : en effet, une Command on l’a compris est une sorte de gros handler d’événement. Pour récupérer les paramètres de l’Event déclencheur (ici entry nous intéresse), on injecte l’event.
  • l’appel à contextView : pour récupérer une référence vers la Stage et ajouter des éléments à la DisplayList, RobotLegs fourni une référence nommée contextView vers la variable du même nom déclarée dans DiaporamaContext.

Note :plutôt qu’un appel à contextView, dans certains cas (menu clic-droit custom, resize de stage, curseur custom, multi-fenêtre sous AIR, etc), il est indiqué de créer un StageMediator.

Aller plus loin

Sur ce tutoriel en particulier, les choses suivantes peuvent être effectuées pour s’entraîner :

  • séparer correctement DiaporamaEvent en plusieurs événements disctincts.
  • ajouter un StageMediator et customiser le menu du clic-droit
  • passer l’adresse du XML en FlashVars
  • ajouter du son : un indice, un son est une View.

Sur un plan plus général :

Cette entrée a été publiée dans Actionscript 3, Flash Platform, Flex, avec comme mot(s)-clef(s) , , , , . Vous pouvez la mettre en favoris avec ce permalien.

5 réponses à Découverte du framework Robotlegs [5/5]

  1. Olivier Gaudin AKA mezigh@mediabox dit :

    Salut

    Petite question (remarque) sur le diaporama .En refaisant le diaporama et en remplaçant le DiaporamaEvent par des signals (as3Signal), je me suis aperçu d’un comportement assez étrange du diaporama lui même.En fait j’ai tout simplement pris d’autres photos avec des tailles différentes les unes par rapport aux autres et le diaporama affiche toujours une image l’une au dessus de l’autre mais ce de manière très (très) étrange . Fais le test en prenant des photos de tailles différentes et tu t’en rendras compte aussi.
    Aussi dans la classe DiaporamaDisplay Il y a une variable qui visiblement ne sert à rien il s’agit de :
    private var _frontContent : Bitmap;
    Peut être que le soucis vient de là …
    En tout cas encore BRAVO pour nous aider tous à progresser.

    A+ Olivier

  2. Arnaud Thorel dit :

    Salut,

    Merci pour ce tuto très intéressant, je l’ai pour ma part adapté à ma façon de développer en utilisant pour les descripteurs de vue du MXML complet, ainsi qu’en déplaçant les eventListener dans le mediator (cela fait gagner un peu de code).
    Le code de vue n’a aucune dépendance avec le reste de l’application, et c’est dommage que ce soit la vue « DiaporamaControls » qui créer le « DiaporamaEvent » plutôt que le médiator qui lui est associé.

    DiaporamaControl.mxml

    DiaporamaControlMediator.as

    public class DiaporamaControlMediator extends Mediator
    {
    [Inject]
    public var controls : DiaporamaControl;

    override public function onRegister():void
    {
    controls.prev.addEventListener(MouseEvent.CLICK, onPrevious );
    controls.next.addEventListener(MouseEvent.CLICK, onNext );
    }

    protected function onNext(event:MouseEvent):void
    {
    dispatch( new DiaporamaEvent(DiaporamaEvent.NEXT) );
    }

    protected function onPrevious(event:MouseEvent):void
    {
    dispatch( new DiaporamaEvent(DiaporamaEvent.PREVIOUS) );
    }
    }

    Un bon compromis entre les deux écritures serait de mettre un évèneent clic générique dclic

  3. Arnaud Thorel dit :

    dans la vue, et que le médiator se bind dessus pour lancer un event.

    Code Vue MXML

  4. Alama dit :

    Sympa ce robotleg! :) Bon, c’est sans doute une bonne façon de rester structuré! Après, je le trouve quand même spécial.. mais bon, c’est sans doute une question d’habitude.. je me suis inventé ma petite structure, n’ayant pas fait des études sur les design paterns ni rien, il a bien fallu être créatif.. j’en suis arrivé à appeler mon système « Skin Based Architecture » on part du skin, suivit de classes qui se plug sur les clip du skin afin de leur donner vie visuellement et d’envoyer des Event .(tout le skin est un SWC). j’ai ensuite des controler par zone de skin, box ou écran différents, ce genre de chose.. et finalement, des classes de type Framework (jpgEncoder, localImage, Utils, etc..).

    Par contre, j’ai ce qui est appelé ici des « Dépendances » !! je ne connaissais pas ce principe d’ « injection » que je n’ai aps encore bien comprise d’ailleurs.. :)

    Mais bon, toutes mes dépendances sont dans MainApplication.as, mon point d’entrée et est unique pour chaque application. Perso, ça ne me pose aucun soucis et je ne vois pas trop pourquoi ce ne serait pas bien.. Mais bon, comme je doute toujours et que je remets en question sans cesse ma structure, je vais encore y réfléchir.. surtout que mes dépendances sont « static » afin que n’importe quel controler puisse éventuellement accéder à un autre controler via Main.xxx . pour en appeler une méthode et éviter les surplus d’Events.. Technique Barbare?? :-)

    Là, je fais de la composition sur mon skin.. mais je me demande si je n’aurais aps un interêt à systématiquement étendre mes Clips de Skin pour faire mes controlers.. C’est la question du moment.. Tu as un avis?

    Merci et encore bravo pour ton blog! ;)
    Alain.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>