Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Périphérique tactile: interpréter un mouvement de glissement dénommé swipe en anglais sur l'élément d'une liste

Compatible Flex 4. Cliquer pour en savoir plus sur les compatibilités.Compatible AIR 2. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Mickael Chaize traduit par autourdeflash (Gilles Doucen - http://autourdeflash.wordpress.com/), le 30 juin 2011

Cet article est une traduction de l'article "Swipe on mobile items" écrit par Mickael Chaize. De nombreux détails ont été ajoutés afin d'éclaircir certains points peu ou pas du tout explicités.

Pré-requis

connaissance: Une première expérience avec Flash Builder est exigée.

niveau utilisateur: Ce tutoriel s'adresse à des utilisateurs de niveau débutant à intermédiaire ayant une première expérience dans le développement d'application web/bureau/mobile.

logiciels: Adobe Flash Builder 4.5+

Sur la vidéo de démonstration, on utilise la distribution 4.5.1 qui apporte une nette amélioration des performances.

Présentation

Je me suis intéressé, cette semaine, aux éléments répétés (renderers) d'une liste pour la réalisation d'une application pour mobile pour un de mes clients. J'ai voulu reproduire un geste classique sur mobile dénommé « swipe » en anglais qui consiste à faire glisser le doigt sur l'écran de la droite vers la gauche sur un élément actif de l'écran. Dans notre cas, on souhaite faire apparaître un menu spécifique en réponse à un geste de ce type au dessus d'un élément d'une liste.

Pour cela, j'ai été amené à étendre le comportement de l'élément répété ce qui est très instinctif avec Flex 4.5 et instaurer une communication entre la liste et les éléments qui la composent. Pour ce tutoriel, j'ai opté pour que la communication soit réalisée par l'envoi d'évènements et l'utilisation d'un dispatcher d'évènements global déclaré dans la classe point d'entrée principal de l'application.

Revenons rapidement sur l'application que l'on souhaite réaliser. L'application propose la gestion basique d'une liste d'employée.

Elle est composée de 3 écrans:

  • un écran de transition au sein duquel nous assurons le chargement de la liste complète des employées sauvegardée sous la forme d'un fichier xml,
  • un écran présentant la liste des employées avec une présentation de type verticale. Un menu de type barre de bouton vient se positionner sur l'élément de la liste sélectionné,
  • un écran présentant une fiche détaillée de l'employé sélectionné.

L'architecture de l'application Flex 4.5 est résumée sur l'aperçu ci-dessous:

Au total, il y a réellement deux vues à réaliser. Le premier écran étant composé d'une image faisant office de « splashscreen » non représenté sur le schéma précédent.

Une vidéo de démonstration est disponible ici. Elle propose un aperçu final de l'application sur différents périphériques android et ios.

Avec Flex 4.5 et le modèle de navigation basé sur les vues apporté par la classe « ViewNavigatorApplication », c'est très rapide.

La classe « ViewNavigatorApplication » et le modèle de navigation basé sur les vues

La classe point d'entrée principal de l'application étend la classe « ViewNavigatorApplication » qui propose une infrastructure simple pour votre application mobile qui repose sur un modèle de navigation basé sur les vues.

Elle assure notamment:

  • la prise en charge des touches matérielles de périphérique,
  • la détection de l’orientation du périphérique,
  • la persistance de session de l’application.

Un modèle de navigation basé sur les vues est caractérisé par une interface utilisateur dans laquelle l’utilisateur final navigue entre une série de vues plein écran en réponse à l’interaction de l’utilisateur. Il s’agit d’un paradigme fréquemment utilisé par les applications mobiles, qui est accompli par le biais de l’utilisation d’un conteneur « ViewNavigator » intégré.

Vous trouverez plus d'informations ici.

L'application initialisée, aucune vue n'est encore chargée. On réalise dans un premier temps le chargement, par le biais de la classe HTTPRequest, de la liste des employées externalisée dans un fichier xml. La liste est disponible ici. Une image faisant office de splashscreen occupe alors la scène le temps du chargement et compose le premier écran.

Une fois notifié de la fin de l'opération par l'envoi de l'évènement « ResultEvent », on commande à l'instance de notre conteneur « ViewNavigator » nommé « navigator » de placer la première vue nommée « sfdcempHomeView » hébergeant notre liste en haut de la pile pour devenir la vue courante. La liste des employées est transmise à la vue en paramètre sous la forme d'un objet de type « ArrayCollection » et sera accessible via la variable « data ». Le splashScreen est automatiquement supprimé de la scène après le chargement de cette première vue.

protected function employeeService_resultHandler(event:ResultEvent):void
{
	// place la vue «  sfdcempHomeView » en haut de la pile pour devenir la vue courante
	navigator.pushView(views.sfdcempHomeView, event.result as ArrayCollection);
}

On crée ensuite une instance du dispatcher d'évènements qui est à la base du modèle d'évènements mis en place. Il sera utilisé au sein de toute l'application pour assurer la communication entre les différents composants et particulièrement entre la liste et les éléments qui la composent.

dispatcher = new EventDispatcher;

<Views> et évènements

La première vue présente la liste des employés. Le fournisseur de données (dataProvider) de ma liste est lié indirectement à la source de donnée définie sous la propriété « data » de ma vue initialisée avec la liste des employés comme évoquée précédemment.

protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
	myEmployees = data as ArrayCollection;
}
<s:List
	dataProvider="{myEmployees}"
	itemRenderer="views.MyIR"
	...
/>

Attardons-nous sur l'élément répété de la liste qui représente la partie la plus intéressante.

On défini d'abord un écouteur pour les évènements de mouvement dit complexe (geste) de type « TransformGestureEvent.GESTURE_SWIPE  » afin d'être notifié lorsque l'utilisateur effectue des mouvements de ce type sur le périphérique tactile.

this.addEventListener(TransformGestureEvent.GESTURE_SWIPE, onSwipe);

En réponse à de tels événements, on souhaite gérer l'affichage d'une barre de boutons représentée par le composant générique « ActionBG ».

<views:ActionBG id="actBar" width="{this.width}" height="{this.height}"/>

Pour cela, on évalue d'abord la direction du glissement par le biais de la propriété offsetX attachée à l'évènement. Une valeur de -1 signifie que le mouvement est dirigé de la droite vers la gauche. Suivant l'état courant d'affichage de la barre de bouton résolu par la variable « FLAGSTATE », la barre de bouton est instanciée si nécessaire avant d'être affichée au dessus de l'élément via une transition de type « déplacer » de la droite vers la gauche.

private var FLAGSTATE:int = 2;
 
protected function onSwipe(event:TransformGestureEvent):void
			{
 
				var myScrollEvent:ScrollingEvent = new ScrollingEvent(ScrollingEvent.SCROLLING_STARTED);
 
				if((event.offsetX == -1) && (FLAGSTATE == 2)){
 
					this.addChild(actBar);
 
					actBar.width = this.width;
					actBar.height = this.height;
					actBar.visible = true;
					actBar.theData = data;
					actBar.addEventListener(FlexEvent.CREATION_COMPLETE, onItemComplete);
					FLAGSTATE = 0;
 
					this.parentApplication.dispatcher.dispatchEvent(myScrollEvent);
				}else{
					if ((event.offsetX == -1)){
						wipeEffect.play();
						this.parentApplication.dispatcher.dispatchEvent(myScrollEvent);
					}
				}
			}

Le reste du code défini les éléments graphiques dont la barre de bouton qui nous intéresse.

Celle-ci est composée de 4 boutons permettant respectivement de la gauche vers la droite:

  • d'afficher le nom complet de l'employé,
  • d'afficher la fiche détaillée de l'employé,
  • non implémenté,
  • de supprimer l'employé de la liste.

Attardons-nous un peu sur le code.

Les données relatives à l'employée sélectionné sont stockées dans la propriété publique nommée « theData » qui est initialisée à l'instanciation du composant.

public var theData:Object;

Les interactions de l'utilisateur au sein de ce composant sont traduits sous forme d'évènements génériques de type « ScrollingEvent » et communiqués au reste de l'application avec l'aide du dispatcher d'évènements créé dans la classe point d'entré principale de l'application que l'on cible via la propriété « parentApplication ». Pour exemple, un clic sur le second bouton à partir de la gauche commande à l'application d'afficher la vue offrant des détails sur l'employé spécifié. La vue est notifiée de cet action par l'envoi d'un événement « ScrollingEvent.TAP_ACTION » par le dispatcher. L'évènement accepte sous la propriété « userObj » l'objet décrivant l'employé ciblé.

protected function button2_clickHandler(event:MouseEvent):void
{
	var tapEvent:ScrollingEvent = new ScrollingEvent(ScrollingEvent.TAP_ACTION);
	tapEvent.userObj = theData as Employee;
	this.parentApplication.dispatcher.dispatchEvent(tapEvent);
}

Dans la vue, on défini les écouteurs relatifs à ces évènements précis générés par l'objet dispatcher et on applique les traitements désirés.

Les évènements de type « ScrollingEvent »

On a besoin de supprimer la barre de bouton affichée sur un élément quand un utilisateur navigue à nouveau à travers la liste ou clique sur un autre de ses éléments. Pour être notifié de telles actions, on s'appuie sur les évènements d'interaction tactile de type « TouchInteractionEvent » attachés à la liste qui sont réinterprétés sous forme d'évènements génériques de type « ScrollingEvent » avant d'être aussitôt relayés via l'objet dispatcher vers tous les éléments de la liste qui se mettent alors à jour.

Dans la vue:

protected function myList_touchInteractionStartHandler(event:TouchInteractionEvent):void
{
  var myScrollEvent:ScrollingEvent = new ScrollingEvent(ScrollingEvent.SCROLLING_STARTED);
  this.parentApplication.dispatcher.dispatchEvent(myScrollEvent);
}
 
(...)
 
<s:List width="100%"
  useVirtualLayout="false" 
  dataProvider="{myEmployees}" 
  creationComplete="myList_creationCompleteHandler(event)" 
  itemRenderer="views.MyIR" 
  touchInteractionStart="myList_touchInteractionStartHandler(event)"    
  height="100%" 
  id="myList"/>

Dans l'élément répété:

this.parentApplication.dispatcher.addEventListener(ScrollingEvent.SCROLLING_STARTED, onScrollAgain);
 
(...)
 
private function onScrollAgain(event:ScrollingEvent):void
{
	if (FLAGSTATE == 1){
		wipeEffectOut.play();
	}
 
}

Les sources

Si vous souhaitez regarder le code source d'un peu plus près, téléchargez le zip du projet ici ou directement depuis l'article original.

Depuis Flex Builder, sélectionnez File > Import > Flash Builder Project pour importer le projet dans l'environnement de travail.

En savoir plus