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

2juil/095

Flex Chart – Modifier la donnée en temps réel, ajout de points de data à un ArrayCollection

Dans l'exemple précédent, je montrais un exemple qui permet de changer le dataProvider d'un Chart à la volée. On va maintenant voir comment conserver le dataProvider original, mais en lui ajoutant simplement des points quand l'utilisateur clique sur un bouton:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" viewSourceURL="srcview/index.html">
	<mx:Script><![CDATA[
		import mx.collections.ArrayCollection;
		[Bindable]
		public var dpac:ArrayCollection = new ArrayCollection([
			{A:2000},
			{A:3000},
			{A:4000},
			{A:4000},
			{A:3000},
			{A:2000},
			{A:6000}
		]);
		public function addDataItem():void {
			var o:Object = {"A":2000};
			dpac.addItem(o);
		}
		private function removeItem():void {
			var i:int = dpac.length;
			if (i > 0) {
				dpac.removeItemAt(i - 1);
			}
		}
	]]></mx:Script>

	<mx:Panel title="Column Chart">
		<mx:ColumnChart id="myChart"
		showDataTips="true"
		height="400"
		width="600"
		dataProvider="{dpac}"
		>
			<mx:series>
				<mx:ColumnSeries yField="A" displayName="Series 1"/>
			</mx:series>
		</mx:ColumnChart>
		<mx:Legend dataProvider="{myChart}"/>
	</mx:Panel>
	<mx:HBox>
		<mx:Button label="Ajouter un Data Item" click="addDataItem();"/>
		<mx:Button label="Supprimer un Data Item" click="removeItem();"/>
	</mx:HBox>
</mx:Application>

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11

Articles similaires

Commentaires (5) Trackbacks (1)
  1. Bonjour Fabien,

    j'ai un soucis concernant l'ajout/suppression de beaucoup de données en même temps sur un CandleStickChart. En fait, je récupère via un service Google des données permettant de construire des chandeliers.

    Exemple d'URL pour Société Générale (GLE) :

    http://www.google.com/finance/getprices?q=GLE&x=EPA&i=86400&p=30Y

    La récupération des données et le render se font bien même si le render est long car sur certaines valeurs j'ai plus de 1500-200 chandeliers...

    Le graph est bien sur dans un premier temps illisible dû au nombre de candles, c'est pourquoi j'ai construit un composant ayant un bouton de zoom+, un bouton de zoom- et une scrollBar afin de "naviguer" dans les données et d'en afficher qu'une partie en construisant un sous dataProvider du dataProvider initial.

    En fait, je cherche à faire ni plus ni moins que ce genre de graph : http://www.zonebourse.com/SOCIETE-GENERALE-4702/analyse_technique-plein/&plein=1

    Sur ce graph, on voit que les zooms se font de manière fluide alors qu'avec mon implémentation, lorsque j'ai 2000 chandeliers ils me faut au bas mot 10-15s avant que le zoom s'effectue...

    Tout est fonctionnel mais tout est très lent^^

    Voici ma fonction de zoom (désolé pour le code) :

    Actionscript:
    1. protected function zoom(event:ZoomEvent):void
    2. {
    3.     var subArrayLength:uint = (_indexesDisplayed.y - _indexesDisplayed.x) + 1;
    4.        
    5.     switch (event.sens)
    6.     {            
    7.         case ZoomEvent.MOINS :
    8.                    
    9.             var toCheckIn:uint = Math.min(toolBar.stepChooser.value, _initialDataAsArrayLength - subArrayLength);
    10.            
    11.             if(toCheckIn == 0)
    12.                 return;
    13.                    
    14.             // Si l'espace entre l'index Y et la fin du tableau en longueur est suffisant pour ajouter autant de valeur que de toCheckIn, alors on ajoute tout à droite
    15.             // Sinon on ajoute à droite (si on peut) un maximum d'items puis à gauche
    16.                    
    17.             if((_initialDataAsArrayLength - _indexesDisplayed.y)&gt;= toCheckIn)
    18.             {
    19.                 (chartRef.dataProvider as ListCollectionView).addAll(new ArrayList(_initialDataAsArray.slice(_indexesDisplayed.y+1, toCheckIn)));
    20.                 _indexesDisplayed.y += toCheckIn;
    21.             }
    22.             else if((_initialDataAsArrayLength - _indexesDisplayed.y)&gt; 0)
    23.             {
    24.                 var toRight:uint = _initialDataAsArrayLength - _indexesDisplayed.y;
    25.                 (chartRef.dataProvider as ListCollectionView).addAll(new ArrayList(_initialDataAsArray.slice(_indexesDisplayed.y+1, toRight)));
    26.                 _indexesDisplayed.y += toRight;
    27.                        
    28.                 var remaining:uint = toCheckIn - toRight;
    29.                 (chartRef.dataProvider as ListCollectionView).addAllAt(new ArrayList(_initialDataAsArray.slice(_indexesDisplayed.x - remaining, remaining)), 0);                       
    30.                 _indexesDisplayed.x -= remaining;
    31.             }
    32.             else
    33.             {
    34.                 (chartRef.dataProvider as ListCollectionView).addAllAt(new ArrayList(_initialDataAsArray.slice(_indexesDisplayed.x - toCheckIn, toCheckIn)), 0);                       
    35.                 _indexesDisplayed.x -= toCheckIn;
    36.             }
    37.  
    38.             toolBar.invalidateSkinState();
    39.                    
    40.             break;
    41.                
    42.         case ZoomEvent.PLUS :
    43.                
    44.             var toCheckOut:uint = Math.min(toolBar.stepChooser.value, subArrayLength - minValuesToDisplay);
    45.                    
    46.             if(toCheckOut&gt; 0)
    47.             {
    48.                 while(toCheckOut--)
    49.                 {
    50.                     (chartRef.dataProvider as ListCollectionView).removeItemAt(0);
    51.                     _indexesDisplayed.x++;
    52.                 }
    53.                                                        
    54.                 toolBar.invalidateSkinState();
    55.             }
    56.                
    57.         break;
    58.                
    59.     }

    _indexesDisplayed est un Point conservant les index affichés par rapport au dataprovider initial (complet).
    chartRef est une référence vers le chart
    stepChooser est un HSlider me permettant de zoomer plus avec tant de valeurs à retirer ou de zoomer moins avec tant de valeurs à ajouter
    _initialDataAsArray est un Array du dataprovider initial afin d'accélérer les opérations dessus
    minValuesToDisplay est le nombre minimum de chandeliers à afficher même si on continue à zoomer +
    subArrayLength est la longueur du sous dataprovider actuellement affiché

    Ma fonction vaut ce qu'elle vaut mais je pense pas qu'elle soit particulièrement mal construite ni mal optimisée.

    Je cherche donc à savoir comment faire pour avoir un graph fluide comme sur le graph présenté en entête. J'ai surement pas le bon algo c'est sûr ^^

    Aurais tu une idée Fabien ?

    Encore désolé pour la longueur du message

    Olivier

  2. Salut Olivier,
    Je préfère les messages longs à ceux qui n'en disent pas assez ;) .

    Pour ton code, je te conseillerai déjà de ne pas travailler directement sur ton ArrayCollection. Il vaut mieux que tu le garde en référence et que tu ailles piocher ce qui t'intéresse et que tu assignes à nouveau le dataProvider de ton chart. Lors des opérations comme addItem ou removeItemAt (dans un while en plus), ton dataProvider va dispatcher des évènements "COLLECTION_CHANGE" (je crois) auquel ton graphique est abonné et qui demande donc à celui-ci de se refresh beaucoup trop souvent. Même si le cycle d'invalidation du rendu des composants Flex va pallier un peu à ce problème, c'est du temps perdu.

    Après, si tu as vraiment du 10-15 secondes de rendu, il faudrait que tu regardes avec le profiler d'où ça vient, il te dira quelle méthode prend autant de temps.

    Fabien

  3. Merci Fabien pour la réponse.

    En fait avant d'utiliser les méthodes addAll etc j'assignais directement un nouveau dataprovider comme tu le suggères mais l'attente était aussi longue. En fait, cela correspond en temps à un premier render du graphique d'environ 2000 chandeliers donc c'est long. Pour tester aussi, j'ai placé un écouteur de l'évenement Event.RENDER sur mon graph et il n'est lancé qu'une fois avec addAll() j'en ai conclu peut être à tord que c'est la méthode la plus optimisée.

    Quoi qu'il en soit, je pense qu'il existe une "combine" différente de celle de modifier le dataprovider du graph afin de zoomer/scroller dessus.

    Si je trace un timer en début et fin de fonction les valeurs diffèrent seulement de quelques ms puis le graph se met à jour 10-15s plus tard donc effectivement c'est bien le render qui prend du temps. Ce que je ne comprends pas c'est pourquoi le graph que j'ai pris en exemple (http://www.zonebourse.com/SOCIETE-GENERALE-4702/analyse_technique-plein/&plein=1) s'affiche aussi vite après un zoom avec autant de valeurs ^^

    C'est pour cela que je pense qu'ils ne changent pas la propriété dataprovider directement mais qu'ils utilisent une autre méthode que je ne connais pas...

    Merci en tout cas, je vais continuer mes tests

    Olivier

  4. Salut Fabien,

    après différents tests, je n'arrive pas à améliorer significativement le temps de rendu des graphs.
    J'ai essayé de régler le min et max de l'axe vertical avant l'affectation du dataprovider afin de minimiser (peut être) le nombres d'opérations de rendu mais je n'aipas gagné grand chose en temps.

    Par contre, je me suis rendu compte d'intéressant et dont je ne me doutais pas : le temps d'affichage d'un graph (ici CandleStick) n'est pas corrélé de façon linéaire à son nombre de données à afficher.

    Dans mon cas de figure, voici quelques "benchmarks" :

    Nb données | temps affichage | temps affichage par donné
    ----------------------------------------------------------------------------------
    5000 | 25s | 5ms
    4000 | 16s | 4ms
    2000 | 5s | 2.5ms
    500 | 0.5s | 1ms

    Dans mon cas de figure, je me contenterais donc d'afficher moins de données si mon graph présente plus de 2000 objets ;)

    A+

    Olivier

  5. Salut Olivier,
    Merci d'avoir partagé tes observations :)

    Fabien


Leave a comment

(required)