Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

26août/110

AIR Mobile – Application TweetDeck (7) – Afficher une liste de Tweet

Dans le dernier tutorial, on a vu comment laisser Flash Builder nous créer les classes nécessaires à l'appel au service Twitter:

AIR Mobile – Application TweetDeck (6) – Configuration des appels HTTP au service Twitter

Dans ce tutorial, on va créer une liste et réaliser l'affichage d'une liste de Tweet récupérés par le service.

Création de la liste

Rendez-vous dans la vue TweetDeckHomeView.mxml et ajoutez un tag "List" à la place du Rect violet:

  <comps:ColumnHeaderIndicator width="100%" height="40" />
  <s:List width="100%" height="100%">

  </s:List>
  <comps:BottomBar width="100%" height="80" />

Appel du service Twitter

Si vous utilisez la vue Design de Flash Builder, vous pourriez glisser directement le service sur votre List pour créer le code d'appel. Mais on va essayer de ne pas passer par la vue Design pour mieux comprendre. Rendez-vous dans la vue "Data/Services" puis faites un clic droit > Generate Service Call:

Boum, Flash Builder vient de vous générer du code dans votre vue:

  <fx:Script>
    <![CDATA[
      protected function search(q:String):void
      {
        searchResult.token = twitter.search(q);
      }
    ]]>
  </fx:Script>

  <fx:Declarations>
    <s:CallResponder id="searchResult"/>
    <twitter:Twitter id="twitter"/>
  </fx:Declarations>

Les objets CallResponder et AsyncToken

Vous avez donc une instance du service Twitter et un objet CallResponder. Si vous ne connaissez pas la classe CallResponder, c'est une classe bien utile. En fait, au lieu d'aller ajouter des écouteurs d'évènement directement sur le service pour écouter "result" et "fault", vous allez déporter cela sur l'instance de CallResponder. C'est votre CallResponder qui va appeler le service Twitter et récupérer un objet "token" de type AsyncToken. Celui-ci permet de repérer la transaction HTTP de manière unique.

Remplis sous: Adobe Air Lire la suite
24août/110

AIR Mobile – Créer une liste avec groupement (type liste de contacts iOS / Android)

Le framework Flex propose en standard, un composant List, qui réagit aux évènements touch pour une utilisation sur mobile. A cette liste, vous pouvez coupler un renderer comme par exemple un renderer de base de Flex, IconItemRenderer qui vous permet d'afficher une image + 2 lignes de texte pour chaque élément.

Mais il y a un autre type de rendu assez populaire sur mobile, qui est de créer une liste à "sections". Par exemple la liste de contacts sous Android ou iOS. Les contacts sont arrangés par ordre alphabétique et rassemblés par la première lettre de leur prénom:

Les sections ne sont pas cliquables, elles servent juste de décoration. Elles ont le plus souvent une taille différente de celle des items. Je ne suis pas un cador du développement natif mais je pense qu'il y a un composant tout prêt pour ce genre d'utilisation. En Flex, il vous faudra le faire vous-même.

Mais comme d'habitude, certains se sont posé la question avant vous (désolé) et on trouvé une solution. Voici quelques liens qui vous seront utiles:

http://corlan.org/2011/07/04/creating-flex-mobile-section-lists/

http://corlan.org/2011/07/11/creating-flex-mobile-lists-part-ii-using-virtualization/

http://www.tink.ws/blog/flex-4-excludeselectionlist/

http://code.google.com/p/tink/source/browse/trunk/flex4.5/spark/src/ws/tink/spark/controls/ExcludeSelectionList.as

http://flexponential.com/2011/06/27/extending-labelitemrenderer-to-look-like-itunes-on-the-ipad/

Voilà, je vous ai bien préparé le travail mais on va aller plus loin en regardant les techniques utilisées.

La technique générale

Pour intégrer ces sections, et que le rendu de la liste soit cohérent, on intègre ces sections directement dans le dataProvider de la liste. Au milieu de vos objets Contact, vous aurez donc d'autres items qui sont là pour la déco. Il faut ensuite déterminer dans votre liste quels sont ces éléments et leur donner un rendu différent.

Le composant ExcluseSelectionList

Le (très bon) blogger de tink.ws propose une implémentation de List.as (par héritage) qui permet d'indiquer au composant quels éléments sont "non-sélectionnables". Vous disposez donc de plusieurs propriétés pour définir cette restriction:

  • excludeType / excludeTypes : Permet de donner une liste de type AS3 à exclure (ex: la classe Header)
  • excludeIndex / excludeIndices : Permet de donner une liste d'index à exclure (ex 1, 10, 30)
  • excludeFunction : Permet de passer une fonction qui va renvoyer un Boolean pour indiquer s'il faut exclure ou pas l'élément. Un peu comme une filterFunction.

Ce composant va se révéler très pratique mais on peut voir que l'itemRenderer créé par flexponential.com contient un petit "trick" qui permet de se passer de cette restriction. En effet, il se contente de ne pas passer l'itemRenderer en état "sélectionné" s'il est sur un header. Un couplage des 2 solutions semble le meilleur compromis.

Utilisation des itemRenderer

C'est la solution la plus naturelle et la plus élégante d'un point de vue code. Suivant l'item passé à l'itemRenderer, on va modifier le rendu.On peut faire ça facilement en MXML mais il vaut mieux le faire en AS3 pour avoir de meilleurs perfs sur mobile.

L'exemple de flexponential explique bien la technique:

http://flexponential.com/2011/06/27/extending-labelitemrenderer-to-look-like-itunes-on-the-ipad/

Ici, on utilise bien la propriété itemRenderer et pas une itemRendererFunction. C'est encore mieux car l'utilisation d'une itemRendererFunction empêche la ré-utilisation des renderers et devient donc moins performante en RAM. Dans l'exemple, le composant MusicLabelItemRenderer est taillé pour cette application puisqu'il connait le type d'item qu'il va afficher. Il peut donc facilement faire la distinction entre un item et un header. Très pratique si vous connaissez votre liste, moins si vous voulez être générique.

En l’occurrence, le renderer est assez complexe puisqu'il permet d'afficher les éléments de manière horizontale avec une ligne séparatrice.

La problématiques des layouts

Voici un problème que je n'avais même pas envisagé pendant la rédaction de cet article. De base, une List utilise un "layout" qui est un VerticalLayout. On obtient donc une liste verticale. Mais sur de plus grands écrans, vous voudrez peut-être utiliser un layout qui vous permet d'afficher plusieurs éléments par ligne, comme un TileLayout:

Si vous modifier votre itemRenderer, vous n'aurez pas un header sur une ligne complète, juste sur la taille d'un item. Le résultat sera donc mauvais. Les billets de corlan.org peuvent vous permettre de résoudre cette problématique en faisant en sorte que votre header prenne toute la largeur. C'est un cas assez exceptionnel mais au moins vous êtes prévenus :) .

Conclusion

Avec ces différents articles, vous avez assez d'informations pour pouvoir créer votre liste. Certaines subtilités comme le layout ou la manière par laquelle on rend les éléments non-sélectionnables sont optionnels, essayez de faire au plus simple.

Dans un futur tutorial, je vais peut-être partager un exemple complet de liste avec groupement avec en bonus le composant de scroll rapide comme sur Android. Pour ceux qui ne voient pas, c'est une liste alphabétique qui vient au dessus de la liste, permettant d'aller directement à la bonne lettre. Très pratique quand on a des dizaines / centaines d'éléments :) .

21juil/117

AIR Mobile – Application TweetDeck (5) – Création d'un composant de défilement horizontal

Notre squelette d'application est bientôt complet, nous avons pour l'instant ajouté la barre de navigation et la barre de header:

AIR Mobile – Application TweetDeck (3) – Ajout de la barre de navigation

AIR Mobile – Application TweetDeck (4) – Création de la barre du haut (header)

Pour la partie centrale, c'est plus complexe. En effet, celle-ci n'est pas un composant "classique". Ce composant ressemble à une liste horizontale mais on ne pourra pas utiliser le composant List pour les raisons suivantes:

  • Une List a tout un mécanisme d'itemRenderer très puissant, mais qui fait que l'affichage peut-être difficile à gérer, notamment à cause de la réutilisation des itemRenderers. Ici, la data est plus un "composant" qu'un "model", cela ne correspond pas vraiment au schéma habituel
  • Un composant List ajoute automatiquement un Scroller. Celui-ci est arrive avec des effets de "bounce & pull" dont nous n'avons pas besoin et qui vont se mettre dans nos pattes. Le composant est au final, un contrôle fin du scroll de l'utilisateur.

Comme le composant List ne nous convient pas, on va créer notre propre composant. Pour nous faciliter la tâche, celui-ci sera un conteneur qui va hériter de HGroup. Cela va nous permettre de faire son intégration de manière très compréhensible en MXML comme ceci:

<comps:HorizontalComponentScroller id="listGroup">
  <s:List id="list1" />
  <s:List id="list2"  />
  <s:List id="list3"/>
  <s:List id="list4"/>
</comps:HorizontalComponentScroller>

On va donc avoir un HGroup, pour lequel on va contrôler à la main, le défilement horizontal à l'intérieur de ce HGroup. Comme de base, les conteneurs Flex 4 n'ajoutent pas automatiquement un Scroller, on va pouvoir faire notre traitement tranquille sans être embêté par certains comportements par défaut. On verra en fin de tutorial que l'on fait en fait une translation en X de ce HGroup à l'intérieur d'un Group pour des raisons pratiques.

Utilisation des Swipe?

Si vous avez un peu manipulé Flex, vous vous êtes peut-être dit : "il est pas très malin, moi je te le fais en 30 secondes en écoutant l'évènement de Swipe gauche / droite !". Ok, j'ai pensé la même chose en débutant le développement de ce composant et pourtant, cela enlève un des comportements intéressants de l'application TweetDeck.

7juil/111

AIR Mobile – Application Pokémon (25) – Conserver la position du scroll vertical d'une List

Dans le tutorial précédent, on a vu comment, grâce à la persistance, on a pu conserver notre liste de Pokémon et éviter de faire une requête SQL. Cela permettait notamment d'éviter que la transition "rame" quand on revient sur la vue liste. D'autant plus agréable pour l'utilisateur:

AIR Mobile – Application Pokémon (24) – Utiliser la persistance pour conserver des données

Si vous essayez l'application, vous allez rapidement voir un comportement un peu lourd. Sur la première vue, vous avez une grande liste (500+ items). Vous faîtes défiler la liste, cliquez sur un pokémon puis vous faîtes back pour revenir sur la liste. Celle-ci va se ré-initialiser et mais vous aurez perdu le défilement. Vu la taille actuelle de la liste, cela peut-être assez lourd à utiliser.

Sauvegarde de la position du défilement

Comme pour le dataProvider, on va conserver la valeur du défilement dans notre objet "PokemonListPersist". Ajoutons donc une propriété à cet objet:

package model {
  import mx.collections.ArrayCollection;

  [RemoteClass]
  public class PokemonListPersist {

    public var dataProvider:ArrayCollection = null;

    public var verticalScrollPosition:Number = 0;

    public function PokemonListPersist() {
    }
  }
}

Cette fois, on ne va pas sauvegarde la valeur à la création de la liste (ça serait un peu bête, non ? ;) ). L'idéal serait de le faire lorsque l'utilisateur quitte la vue.

Si vous avez suivi les tutoriaux Flex de flex-tutorial, vous savez qu'il y a un évènement qui nous prévient ce moment. Celui-ci est propagé par la vue et se nomme "viewDeactivate". Ajoutons un handler que l'on va nommer "onViewDeactivate":
han

Et on récupère notre PokemonListPersist puis on lui affecte notre valeur de scroll vertical:

...
  protected function onViewDeactivate(event:ViewNavigatorEvent):void {
	if (data) {
	  var plp:PokemonListPersist = PokemonListPersist(data);
	  plp.verticalScrollPosition = list.dataGroup.verticalScrollPosition
	}
  }
  ...

Chargement de la position

Maintenant que l'on a notre position sauvegardée, on va la charger, comme on a rechargé notre dataProvider:

...
_dp.filterFunction = filterByName;
list.dataGroup.verticalScrollPosition = plp.verticalScrollPosition;
return;
...

On teste notre application en debug, la valeur conservée dans notre "plp.verticalScrollPosition" est la bonne. Pourtant, notre scroll est toujours en haut.

1juil/112

AIR Mobile – Restreindre une List à un scrolling horizontal uniquement

Lorsque vous compilez une application Flex 4.5.1 qui utilise le thème Mobile, celle-ci se comporte différemment, notamment pour réagir aux évènements "touch". Les List se comportent elles aussi différemment, dans leur comportement lors du scrolling. Celui-ci a des effets "bounce and pull" qui font comme si la liste rebondissait lorsqu'elle touche une extrémité.

Le rendu est plutôt sympa et cela ressemble au rendu des listes des applications natives Android et iOS.

Le problème

Souvent dans les exemples des Twitter-Like, on voit des listes verticales. Pour celles-là, pas de problème, l'effet est le bon. Au lieu de donner un VerticalLayout à votre List, donnez lui maintenant un HorizontalLayout.

Testez votre application et vous allez avoir un comportement assez étrange. Vous aurez bien un scrolling horizontal mais la liste va reproduire les effets bounce and pull sur l'axe vertical. Il vous sera donc très difficile de faire un déplacement parfaitement horizontal.

En soi, ce n'est pas vraiment un bug, dans ce billet de la JIRA Adobe, un des ingénieurs du SDK explique que c'était le comportement escompté mais qu'il aurait du être mieux documenté (même si honnêtement, pour ce genre de problème, je n'irai pas voir la doc):

A horizontal mobile list does not have bounce/pull effects on its right and left boundaries when the content is smaller than the size

La solution

La solution est en fait plutôt simple. Il suffit de donner une verticalScrollPolicy à "off" à la liste pour ne pas avoir l'effet. Petit exemple:

<s:List id="colorList" dataProvider="{colorsArray}" width="100%" itemRenderer="ColorRenderer" y="40" selectedIndex="0" verticalScrollPolicy="off">
  <s:layout>
    <s:HorizontalLayout  />
  </s:layout>
</s:List>

Voilà, c'est en fait plutôt simple mais j'ai eu ce problème dans un de mes projets et cela m'a bien pris 30 minutes. Merci à ce billet pour m'avoir guidé vers la bonne solution:

Getting scrolling right in a mobile horizontal list

Remplis sous: Adobe Air, List 2 Commentaires