Arhiva pentru categoria 'Tutoriale'

Mar2nd2008

Tutorial IV : Evenimente

Flexibilitate şi asincronicitatea sunt virtuţile pe care programarea orientată pe obiecte şi eveniment le accentuează. Limbajele în care se programează interfeţe grafice dau posibilitatea de a gestiona evenimentele, asemenea sistemelor de operare - programe dirijate de evenimente pe cel puţin două nivele. La cel mai de jos nivel, întreruperile se comportă ca nişte handlere de evenimente hardware, cu procesorul în rol de dispecer. Coordonarea proceselor se face transmiţând către procese utilizator date şi facilitând accesul la întreruperile software. În general, handlerele de eveniment sunt apelate ca răspuns la stimuli externi, spre exemplu mouse-ul sau tastatura. Tot handlerele de evenimente folosesc o coadă a evenimentelor folosind structuri de tipul primul intrat primul ieşit (FIFO) pentru a reţine evenimentele care nu au fost procesate.
Flex nu face excepţie de la regulile de bază ale programării orientate pe obiect şi eveniment, astfel că evenimentele reprezintă o parte esenţială a dezvoltării de aplicaţii. Modelul evenimentelor Flex este bazat pe DOM (Document Object Model) de nivel 3, prin care W3C defineşte o platformă generică şi un sistem de evenimente independent de limbaj ce permite adăugarea handlerelor de eveniment, descrie structura în model arborescent şi defineşte informaţiile contextuale pentru fiecare eveniment (detalii folosite în gestionarea evenimentului respectiv).
Componentele au definite evenimente native care pot fi generate şi totodată ascultă alte evenimente. Într-o aplicaţie Flex, evenimentele sunt gestionate cu ActionScript, scris bineînţeles în tag sau în clasele care sunt instanţiate. Toate controalele şi containerele Flex sunt subclase ale DisplayObject şi sunt dispuse într-o ierarhie de obiecte vizibile cunoscute ca display list. Această listă conţine toate elementele pe care playerul le va desena pe ecran - obiectele dispuse arborescent corespund nodurilor din structura DOM şi sunt parcurse de evenimente generate de player. Atunci când un eveniment este creat, există trei faze pe care Flex le foloseşte pentru a determina dacă există ascultători : faza de capturare, în care playerul verifică fiecare nod începând de la nodul rădăcină până la părintele direct al nodului ţintă pentru a vedea dacă este înregistrat un ascultător pentru a trata evenimentul ; faza ţintă, în care evenimentul este trimis nodului ţintă ; faza bulelor, în care playerul verifică în ordinea inversă fazei de capturare - începe de la părintele direct al nodului ţintă şi sfârşeste la nodul rădăcină, căutând ascultători.
Evenimentele se clasifică astfel: evenimente de sistem, care se propagă atunci când codul este executat şi evenimente propagate de utilizator - care apar în momentul în care utilizatorul interacţionează cu aplicaţia (butoane, elementele formelor, etc). Gestionarea evenimentelor de sistem presupune ascultarea evenimentelor la execuţia codului, fără a fi necesară aşteptarea interacţiunii utilizatorului cu aplicaţia. Trei dintre cele mai importante evenimente de sistem, moştenite de orice obiect care reprezintă o subclasă a UIObject sunt creationComplete, initialize şi show.
Să parcurgem următorul exemplu:

Actionscript:
  1. </mx><mx xmlns:mx="http://www.adobe.com/2006/mxml" creationcomplete="evenimente_txt.text += '\n a apărut evenimentul creationComplete al aplicaţiei'" initialize="evenimente_txt.text += '\n a apărut evenimentul initialize al aplicaţiei'">
  2. </mx><mx title="Gestionarea evenimentelor de sistem">
  3. </mx><mx editable="false" height="100%" width="100%" id="evenimente_txt">
  4. </mx>

Exemplu se foloseşte de evenimentele de sistem creationComplete (atunci când un constructorul unui obiect, subclasă a lui UIObject, şi-a terminat execuţia şi acesta este "desenat" pe ecran) şi de initialize (momentul în care un obiect, subclasă a lui UIObject, şi-a terminat execuţia constructorului, dar obiectul nu este desenat pe ecran). Aceleaşi evenimente pot fi capturate şi pentru componenta Panel:

Actionscript:
  1. <mx title="Gestionarea evenimentelor de sistem">
  2. creationComplete="eveniment_txt.text += ('\n componenta Panel, evenimentul creationComplete ')"
  3. initialize="eveniment_txt.text += ('\n componenta Panel, evenimentul initialize')"&gt;

Exemplul se foloseşte de scrierea codului ActionScript în maniera numită inline, adică nu definim un handler de eveniment care să includă codul ActionScript întrucât operaţiunile pe care le facem sunt simple. În momentul execuţiei codului de mai sus, putem observa că ierarhia evenimentelor de sistem face în aşa fel încât evenimentele aplicaţiei sunt întotdeauna lansate ultimele - logica fiind simplă : aplicaţia este ultima care anunţă evenimentele de sistem, întrucât obiectele pe care le conţine trebuie să anunţe primele aceste evenimente, ele făcând parte din aplicaţie.
Din acest motiv, creationComplete şi initialize ale aplicaţiei, pot fi folosite pentru a întreprinde acţiuni cum ar fi cereri de date către server.
Să ne ocupăm şi de evenimentele care provin din acţiunile utilizatorului, handlerul de eveniment va fi scris în interiorul tag-ului întrucât

Actionscript:
  1. </mx><mx xmlns:mx="http://www.adobe.com/2006/mxml">
  2. </mx><mx title="Gestionarea evenimentelor utilizator">
  3. </mx><mx id="myLabel" text="apăsaţi pe buton!">
  4. </mx><mx>
  5. </mx><mx id="myButton" label="faceţi click!" click="handlerButon()">
  6. </mx><mx label="Legătura 1" click="handlerLinkButton()">
  7. </mx>
  8.  
  9. <mx>
  10. &lt;![CDATA[
  11. private function handlerButon():void {
  12. myLabel.text="Butonul a fost apăsat!";
  13. }
  14. private function handlerLinkButton():void {
  15. myLabel.text = "Legătura 1 a fost accesată!";
  16. }
  17. ]]&gt;
  18. </mx>

După cum se poate observa, fiecare dintre butoane are handler-ul lui de eveniment, care este chemat în momentul în care utilizatorul întreprinde acţiunea de a apăsa butonul stânga al mouse-ului deasupra butonului în cauză. Este de la sine înţeles că putem folosi un handler unificat, să zicem pentru toate butoanele dintr-un panel, iar în funcţie de identificarea acestora să tratăm evenimentele.
Clasa flash.events.Event conţine informaţii despre evenimentul apărut - de fiecare dată când un eveniment apare, clasa generează automat un obiect care conţine informaţii despre eveniment şi care poate fi folosit în handler. Handler-ul trebuie să aibă drept parametru un astfel de obiect făcând astfel posibil să accesăm proprietăţile în cadrul lui. Câteva dintre proprietăţile obiectelor eveniment sunt comune diverselor tipuri de evenimente, aşa cum unele proprietăţi sunt specifice respectivului eveniment.
Proprietăţile comune sunt: type de tip String - conţine numele evenimentului apărut ; target de tip Event - conţine instanţa componentei care a propagat evenimentul ; target.id de tip String - conţine numele instanţei (definit prin proprietatea id) şi care poate fi folosit atunci când definim un handler comun, aşa cum am menţionat anterior.
Parametrul handler-ului poate fi în general obiect al clasei flash.event.Event sau se poate folosi o subclasă specifică evenimentului respectiv, aşa cum sunt MouseEvent şi DataEvent. Reţineţi că pentru a putea folosi aceste subclase, este necesar să le importaţi înainte de folosire : import flash.events.MouseEvent; clasa flash.event.Event este implicit inclusă în aplicaţie, din acest motiv ea nu trebuie importată înainte de folosire.

Vom modifica exemplul de mai sus pentru a folosi cele prezentate, înlocuind eticheta myLabel cu o componentă TextArea (myTA) care să ne permită să vizualizăm detalii despre evenimentul apărut:

Actionscript:
  1. <mx xmlns:mx="http://www.adobe.com/2006/mxml">
  2. </mx><mx title="Gestionarea evenimentelor utilizator">
  3. </mx><mx id="myTA">
  4. </mx><mx>
  5. </mx><mx id="myButton" label="faceţi click!" click="handlerButon(event)">
  6. </mx><mx label="Legătura 1" click="handlerLinkButton(event)">
  7. </mx>
  8.  
  9. <mx>
  10. &lt;![CDATA[
  11. private function handlerButon(e:Event):void {
  12. myTA.text="Butonul a fost apăsat!";
  13. myTA.text+= "\n\n\t Tip eveniment: " + e.type;
  14. myTA.text+= "\n\n\t Ţintă eveniment: " + e.target;
  15. myTA.text+= "\n\n\t ID ţintă: " + e.target.id;
  16. }
  17. private function handlerLinkButton(e:Event):void {
  18. myTA.text = "Legătura 1 a fost accesată!";
  19. myTA.text+= "\n\n\t Tip eveniment: " + e.type;
  20. myTA.text+= "\n\n\t Ţintă eveniment: " + e.target;
  21. myTA.text+= "\n\n\t ID ţintă: " + e.target.id;
  22. }
  23. ]]&gt;
  24. </mx>

Aşa cum am menţionat anterior, putem folosi o subclasă a clasei Event, modificând codul handler-elor după cum urmează:

Actionscript:
  1. <mx>
  2. &lt;![CDATA[
  3. import flash.events.MouseEvent;
  4. private function handlerButon(e:MouseEvent):void {
  5. myTA.text="Butonul a fost apăsat!";
  6. myTA.text+= "\n\n\t Tip eveniment: " + e.type;
  7. myTA.text+= "\n\n\t Ţintă eveniment: " + e.target;
  8. myTA.text+= "\n\n\t ID ţintă: " + e.target.id;
  9. }
  10. private function handlerLinkButton(e:MouseEvent):void {
  11. myTA.text = "Legătura 1 a fost accesată!";
  12. myTA.text+= "\n\n\t Tip eveniment: " + e.type;
  13. myTA.text+= "\n\n\t Ţintă eveniment: " + e.target;
  14. myTA.text+= "\n\n\t ID ţintă: " + e.target.id;
  15. }
  16. ]]&gt;
  17. </mx>

De această dată, am folosit clasa MouseEvent pentru că este strict specifică evenimentelor apărute ca urmare a interacţiunii utilizatorului prin intermediul mouse-ului. Tipurile stricte asigură programatorul că în momentul compilării, proprietăţile care nu fac parte din clasa respectivă vor atrage după sine un mesaj de eroare şi totodată, imbunătăţeşte performanţele aplicaţiei.

Următorul pas pe care îl vom face este acela de a crea un ascultător pentru un anumit eveniment, folosind direct limbajul ActionScript. Obiectelor li se pot "ataşa" ascultători de evenimente folosind metoda addEventListener() care face parte din clasa EventDispatcher. Înregistrarea unui handler pe această cale permite controlul avansat al gestiunii de evenimente, permiţând spre exemplu să gestionăm evenimente pentru mai multe componente (crescând flexibilitatea codului) sau să adăugăm mai mulţi ascultători aceleiaşi componente.
Sintaxa generală pentru a adăuga un ascultător este : numele_instanţei.addEventListener(tipEveniment:String, numeAscultător:Function, foloseşteFazaCapture:Boolean, prioritate:int, referinţăSlabă:Boolean) - în care parametrii sunt tipul evenimentului, numele funcţiei ascultător (chemată atunci când evenimentul pe care-l ascultăm apare), foloseşteFazaCapture arată dacă ascultătorul va intra în acţiune în faza de capturare (descrisă mai sus) sau în faza ţintă/bulelor. Valoarea implicită a acestui parametru este false indicând că ascultătorii funcţionează corect în fazele ţintă şi bulelor - montarea unui ascultător pentru faza de capturare fiind folositoare pentru evenimente de tip low-level, doar atunci când se defineşte o componentă ce gestionează astfel de evenimente.
Parametrul prioritate indică prioritatea pe care o are ascultătorul şi se foloseşte pentru cazul în care montăm mai mulţi ascultători pentru acelaşi eveniment, aceleiaşi componente - stabilind astfel ordinea în care ascultătorii sunt chemaţi în cazul apariţiei evenimentului în cauză. Valoarea implicită este 0. Parametrul referinţăSlabă stabileşte dacă ascultătorul va fi colectat de garbage-collector sau nu (referinţăSlabă, implicit false indică faptul că ascultătorul nu va fi colectat).

Despre garbage-collector, în câteva cuvinte : este acel proces responsabil pentru dealocărea memoriei utilizate de obiecte care nu mai sunt folosite de aplicaţie. Dacă celelalte obiecte active nu au referinţă spre un terţ obiect, acesta din urmă va fi considerat inactiv şi în consecinţă el va ajunge în garbage-collector. O explicaţie mai bună ar fi că atunci când lucrăm cu obiecte ce au tipuri care nu sunt primitive (altele decât Boolean, String, uint, int, Number) vom folosi întotdeauna referinţa către obiecte şi nu obiectele însele - astfel încât atunci când ştergem o "variabilă" de fapt ştergem referinţa către un obiect, nu obiectul în sine. Exemplu:

Actionscript:
  1. // facem o referinţă a unui nou obiect
  2. var referinta:Object = {proprietate:"o valoare"}
  3. // copiem referinţa creată
  4. var copieReferinta:Object = a;
  5. // acum ştergem referinţa
  6. delete(referinta);
  7. // verificăm dacă obiectul este încă referinţă în copia referinţei
  8. trace(copieReferinta.proprietate);

Aşa cum veţi observa, copia referinţei va întoarce valoarea proprietăţii ("o valoare"), ca dovadă a faptului că obiectul pe care prima referinţă îl indica nu a fost şters, chiar dacă referinţa a fost ştearsă. Acest obiect va ajunge în garbage-collector odată ce nu va mai exista nici o referinţă către el.
Este foarte important ca în faza de dezvoltare a unei aplicaţii, programatorul să definească obiectele cât mai inerte cu putinţă, întrucât nu există nici un control asupra garbage-collector-ului, fapt care atrage după sine comportamente nedorite ale aplicaţiei în momentul în care nu există nici o referinţă către un anume obiect. Mai multe informaţii despre strategii de gestiune a resurselor puteţi citi aici (articol în limba engleză).

Revenind la evenimente, să aplicăm ceea ce-am prezentat:

Actionscript:
  1. <mx xmlns:mx="http://www.adobe.com/2006/mxml" creationcomplete="init()">
  2. </mx><mx>
  3. &lt;![CDATA[
  4. // importăm, aşa cum am spus, clasa evenimentului specific
  5. import flash.events.MouseEvent;
  6. private function init():void {
  7. //montăm evenimentul în momentul în care constructorul aplicaţiei şi-a închieat execuţia
  8. //şi aplicaţia a fost desenată
  9. unButton.addEventListener("click",unClickHandler);
  10. //schimbăm eticheta pentru a şti că ascultătorul este montat
  11. unLabel.text = "Ascultătorul este montat!";
  12. }
  13. private function unClickHandler(e:MouseEvent):void {
  14. //handler-ul de eveniment montat la butonul unButton
  15. unLabel.text="Butonul a fost apăsat!";
  16. }
  17. ]]&gt;
  18. </mx>
  19. <mx title="Exemplu montare ascultător">
  20. </mx><mx id="unLabel" text="Priveşte aici...">
  21. </mx><mx id="unButton" label="Execută">
  22. </mx>

După cum se observă cu uşurinţă, prin folosirea metodei addEventListener() s-au separat complet partea VIEW de partea CONTROLLER ale aplicaţiei. Termenii VIEW şi CONTROLLER fac referire la modelul de dezvoltare (design-pattern) MVC : Model-View-Controller. În aplicaţii complexe care lucrează cu cantităţi apreciabile de date, este de preferat să separăm datele (model) de interfaţa cu utilizatorul (view) astfel încât schimbările aduse interfeţei să nu interfereze cu schimbările datelor, totodată reorganizarea datelor să nu afecteze interfaţa. MVC rezolvă această problemă prin separea accesului la date şi logistica organizării datelor de nivelul în care se prezintă datele utilizatorului şi interacţiunea pe care interfaţa trebuie să o asigure, legătura dintre acestea fiind ţinută printr-un intermediar : controller-ul.
Modul în care Flex gestionează modelele de date se bazează pe MVC.

În continuare, vom descrie o clasă ActionScript externă care defineşte metode ce pot funcţiona ca şi ascultători - chiar dacă clasele ActionScript nu se pot comporta ca ascultători, metodele pe care o clasă le defineşte pot fi folosite în acest scop. Este suficient să creăm o clasă simplă, cu un constructor vid dar care importă flash.events.Event şi defineşte metodele pe care le vom folosi pe post de ascultători.

Actionscript:
  1. package ro.flexug.tutorial4{
  2. import flash.events.Event;
  3. import mx.controls.Alert;
  4. public class Ascultator{
  5. public function Ascultator(){
  6. }
  7. public static function metodaHandler(event:Event):void{
  8. Alert.show("Butonul a fost apăsat!");
  9. }
  10. }
  11. }

Considerăm cunoscute toate noţiunile ce sunt necesare pentru declararea pachetului şi clasei în cauză (vezi tutorialul I). Folosindu-ne de codul de mai sus, vom crea aplicaţia bazată pe exemplul anterior:

Actionscript:
  1. <mx xmlns:mx="http://www.adobe.com/2006/mxml" creationcomplete="init()">
  2. </mx><mx>
  3. &lt;![CDATA[
  4. import flash.events.MouseEvent;
  5. import ro.flexug.tutorial4.Ascultator;
  6. private function init():void {
  7. unButton.addEventListener(MouseEvent.CLICK,unClickHandler);
  8. unLabel.text = "Ascultătorul este montat!";
  9. //aici adăugăm ascultătorul bazat pe clasă
  10. unButton.addEventListener(MouseEvent.CLICK,Ascultator.metodaHandler);
  11. }
  12. private function unClickHandler(e:MouseEvent):void {
  13. //handler-ul de eveniment montat la butonul unButton
  14. unLabel.text="Butonul a fost apăsat!";
  15. }
  16. ]]&gt;
  17. </mx>
  18. <mx title="Exemplu montare ascultător">
  19. </mx><mx id="unLabel" text="Priveşte aici...">
  20. </mx><mx id="unButton" label="Execută">
  21. </mx>

Aşadar, avand metodaHandler() definită static aceasta poate fi invocată fără a instanţia clasa în ActionScript.

Feb29th2008

Tutorial III : Declararea Stilurilor in Flex, CSS

Cerinte

Pentru a putea intelege pe deplin acest tutorial sunt necesare oarecare cunostinte de CSS (cascade style-sheet), Flex, nivel de incepator si, desigur, o minte deschisa si multa vointa.

CSS poate fi descris ca o metoda de a controla modul de prezentare a unui document la nivel de formatare. De aceea, cel mai des va fi folosit pentru a controla modul in care continutul va fi afisat in pagina, stiulurile aplicate, de la tipul fontului pana la marimea ferestrelor si a bordurilor. CSS a fost adoptat in totalitate de Adobe pentru a putea controla o componenta a unei aplicatii, ori a aplicatiei la nivel global.

Majoritatea stilurilor definite intr-o aplicatie flex sunt mostenite de componentele ei, astfel se permite programatorului definirea unor reguli de stil de la cel mai intalt nivel pana la baza. Mai mult decat atat, externalizarea stilurilor ajuta la organizarea dezvoltarii unui proiect, astfel incat programatorul sa se poata ocupa de functionalitate, iar designerul sa se poata ocupa de interfata. Aceste stiluri vor fi continute de toate controalele si ferestrele din containerul principal, facand aplicatia foarte dinamica, respectand totodata principiul mostenirii definit nu numai in standardele CSS, dar si in standardele OOP.

Din nefericire, exista componente care nu pot fi accesate, si care vor trebui definite individual pentru fiecare element in parte. Astfel de caracteristici sunt: inaltimea (height) si latimea(width) care apartin unei clase de baza a componentelor, UIComponent.

Ce metode se folosesc pentru a defini stilurile in Flex?

Flex permite definirea stilurilor in 2 moduri esentiale, folosierea selectoarelor globale din CSS ( si selectorii pe care dorim sa ii definim), si managerul de stiluri pe care actionscript-ul il pune la dispozitie folosind clasa StyleManager, definit in pachetul mx.styles.

Desigur, ca si in XHTML, stilurile pot fi continute in interiorul aplicatiei, ori extern reprezentand in fapt, respectarea standardelor web 2.0.

Compilarea stiulurilor

In Flex sunt mai multe metode de a introduce definitii pentru stiuluri, ca si in CSS, este introdus in cod (inline), extern si incorporat. In acelasi timp e de retinut ca sunt caracteristici comune tuturor tagurilor (spre exemplu, tuturor etichetelor sau tuturor butoanelor unei aplicatii), si caracteristice unice, specifice (care fac referire sa zicem la o anume eticheta sau care definesc o clasa de stil ce poata fi folosita pentru anumite componente). Sa vedem un exemplu in care aplicam stiluri:

Actionscript:
  1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  2. <mx:Panel title="Panou personalizat" width="400" height="400">
  3. <mx:Label text="Iata o fraza pe care o putem personaliza in functie de preferinte" />
  4. </mx:Panel>
  5. </mx:Application>

Vom introduce stilul utilizand tagurile :

Actionscript:
  1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  2. <strong><mx:Styles></strong>
  3. <strong></mx:Styles></strong>
  4. <mx:Panel title="Panou personalizat" width="400" height="400">
  5. <mx:Label text="Iata o fraza pe care o putem personaliza in functie de preferinte" />
  6. </mx:Panel>
  7. </mx:Application>

In aceste taguri vom introduce proprietatea pe care o definim si valoare acesteia :

Actionscript:
  1. <mx:Style>
  2. <strong>Application {</strong>
  3. <strong>backgroundColor: #CCCCCC;</strong>
  4. <strong>}</strong>
  5. </mx:Style>

Aceasta va fi o proprietatea ce va fi mostenita de toate componente aplicatiei Flex. Iata in schimb o proprietate ce nu va mai fi mostenita:

Actionscript:
  1. <mx:Style>
  2. Application {
  3. backgroundColor: #FFCCFF;
  4. <strong>textDecoration: underline;</strong>
  5. }
  6. </mx:Style>

Proprietatea textDecoration nu va fi mostenita in nici un fel, de-a lungul ierarhiei aplicatiei. Pentru a o mosteni la nivelul tuturor componentelor aplicatiei folosim selectorul global:

Actionscript:
  1. <mx:Style>
  2. global  {
  3. fontFamily:'Trebuchet';
  4. fontSize:14px;
  5. textDecoration: underline;
  6. fontWeight: bold;
  7. fontStyle: italic;
  8. }
  9. Application {
  10. textDecoration: underline;
  11. backgroundColor: #FFCCFF;
  12. }
  13. </mx:Style>

O lista completa a selectorilor pentru fiecare dintre componentele unei aplicatii Flex poate fi vazuta aici.
Folosirea Actionscript pentru a defini stilurile

Un alt mod de a defini selectori globali in interiorul unei aplicatii flex este prin intermediul pachetului mx.styles.StyleManager ce va trebui chemat la inceputul scriptului pentru a putea folosi aceasta clasa.

Cu ajutorul metodei getStyleDeclaration(selector:Style) se va putea identifica proprietatea tipului de interfata pe care il setam (buton, panouri, text, etc). Pentru a putea seta aceasta proprietate ce ne intereseaza, vom utiliza metoda setStyle().

Iata un exemplu de folosire a clasei styleManager.

StyleManager.getStyleDeclaration(global).setStyle(fontSize, 12);

Vom pune accent pe codul folosit pentru a defini aceste caracteristici, introdus in aplicatie in interiorul tagurilor . Iata cum definim caracteristicile globale ale unui panou in flex:

Actionscript:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  3. <mx:Script>
  4. <![CDATA[
  5. import mx.styles.StyleManager;
  6. private function initiazaStil():void {
  7. // stilul global se va aplica la toate controalele din aplicatie
  8. StyleManager.getStyleDeclaration("global").setStyle("fontSize",12);
  9. StyleManager.getStyleDeclaration("global").setStyle("fontStyle","italic");
  10. StyleManager.getStyleDeclaration("global").setStyle("fontWeight","bold");
  11. StyleManager.getStyleDeclaration("global").setStyle("textDecoration", "underline");
  12. StyleManager.getStyleDeclaration("global").setStyle("fontFamily","Arial");
  13. }
  14. ]]>
  15. </mx:Script>
  16. <mx:Panel title="Folosirea clase styleManager din ActionScript">
  17. <mx:Label text="Tex de incercare a stilurilore globale" />
  18. </mx:Panel>
  19. </mx:Application>

Am definit mai sus functia initiazaStil() care va seta toate proprietatile globale ce ne intereseaza, iar pentru a putea chema aceasta functie ne vom folosi de evenimentul de sistem creationComplete() ce va fi chemat la inceputul aplicatiei. Iata cum vor deveni declaratiile aplicatiei:

Pentru cazul in care vom incarca un stil extern, este necesar sa-l aplicam aplicatiei inainte de initializarea acesteia, dar mai ales va trebui sa suprascriem getter-setter-ul aplicatie pentru ca aceasta sa nu porneasca inainte de aplicarea stilului incarcat.

Iata un exemplu:

Actionscript:
  1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  preinitialize="loadStyle()">
  2. private function loadStyle():void {
  3. var eventDispatcher:IEventDispatcher = StyleManager.loadStyleDeclarations("exemplu_tema.swf");
  4. eventDispatcher.addEventListener(StyleEvent.COMPLETE, completeHandler);
  5. }
  6. private function completeHandler(event:StyleEvent):void {
  7. //aici pot fi inserate operatiuni de update la diverse componente, daca sunt necesare spre exemplu,  update-uri de styleName
  8. super.initialized = true;
  9. }
  10.  
  11. override public function set initialized(value:Boolean):void {
  12. // asteapta ca CSS-ul sa fie incarcat
  13. }

Completari
Este de notat ca, in ciuda faptului ca flex a integrat foarte bine CSS-ul, nu toate specificatiile pot fi setate prin intermediul stilurilor globale, ci vor trebui definite pentru fiecare element in parte, atunci cand vor fi create. Este cazul inaltimii si latimii panourilor, elementelor height si width, fiind componente ale clasei UIComponent.
In cele din urma, va trebui sa punctez, ca si in CSS si XHTML, o proprietate CSS care este inline va avea prioritate fata de declaratie ei in functia global sau in aplicatie.

Feb24th2008

Tutorial II: Data Binding

În etapa de dezvoltare a unei aplicaţii Flex, una dintre cele mai importante şi mai des utilizate tehnici este data binding – procesul prin care un obiect ce conţine date este „legat” de o componentă. În acest fel se asigură independenţa operaţiunilor cu date de reactualizarea informaţiilor afişate de componentă – concret, atunci când datele din obiectul legat se schimbă, componenta actualizează informaţiile pe care le afişează din acel obiect, lăsându-i programatorului libertatea de a se concentra exclusiv pe prelucrarea datelor. Data binding în Flex deţine puterea: pe lângă legături între obiecte simple, putem crea şi legături între funcţii şi expresii ActionScript – binding reprezentând „rigidizarea” elementelor „flexibile”.

 

În Flex sunt trei posibilităţi de a crea o astfel de legătură: utilizând acoladele {}, folosind tag-ul <mx:Binding> sau folosind ActionScript şi packet-ul BindingUtils. Nu folosim termenul metodă (în locul lui posibilitate) în mod intenţionat, deoarece prezentarea în contextul programării orientate pe obiecte ne obligă la acest lucru.

Cea mai simplă şi mai utilizată manieră, folosind parantezele acoladă ({}), permite legături rapide între proprietăţile obiectelor, spre exemplu :

<mx:Label id="myLabel" text="Hello World"/>

<mx:Label text="{myLabel.text}" />

După cum puteţi observa, componenta sursă trebuie să aibă o denumire (id=”myLabel”) pentru a-i putea accesa proprietăţile, în cazul nostru proprietatea text – pe care o legăm în componenta destinaţie. Ori de câte ori proprietatea text a componentei sursă myLabel se va schimba şi cea de-a doua componentă (destinaţie) va prelua în proprietatea ei (text) valoarea modificată.

A doua manieră, folosind tag-ul MXML <mx:Binding>, exprimă mult mai clar legătura descrisă mai sus, întrucât proprietăţile tag-ului fac referire directă şi în limbaj natural la sursă, respectiv destinaţie:

<mx:Label id="myLabel" text="Hello World"/>

<mx:Label id=”myLabelDest” text="{myLabel.text}" />

<mx:Binding source=" myLabel.text" destination=" myLabelDest.text"/>

De reţinut că această manieră obligă programatorul să denumească ambele componente pentru a le putea accesa. Ca o informaţie suplimentară, toate componentele sunt denumite automat de către compilator, în cazul în care programatorul a omis să-i atribuie un identificator (id).

Ultima dintre posibilităţi poate fi folosită în ActionScript, utilizând clasa mx.binding.utils.BindingUtils : BindingUtils.bindProperty(myLabel, "text", myLabelDest, "text");

Clasa foloseşte o metodă statică (bindProperty) pentru a crea legătura dintre proprietăţi implementate ca variabile sau o metodă specifică setter-getter-elor (bindSetter) dacă este vorba despre proprietăţi implemetate prin intermediul getter-setter. De reţinut : metodele statice acţionează la nivelul clasei în care este definită, pe când metodele în general acţionează la nivelul instanţei clasei.

 

Să pornim un nou proiect, în care să inserăm un ArrayCollection (care este în fapt, un array complex) :

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

<mx:ArrayCollection id="myAC">

<mx:source>

<mx:Object siteURL="http://www.adobe.com" siteNume="Adobe"/>

<mx:Object siteURL="http://www.google.com" siteNume="Google"/>

<mx:Object siteURL="http://www.yahoo.com" siteNume="Yahoo"/>

<mx:Object siteURL="http://flex.akademia.ro" siteNume="ROFUG"/>

<mx:Object siteURL="http://www.badu.ro" siteNume="Badu"/>

</mx:source>

</mx:ArrayCollection>

</mx:Application>

ArrayCollection-ul nostru conţine obiecte care au fiecare definite proprietăţile siteURL şi siteNume. Vom defini o legătură între ArrayCollection şi o etichetă care afişează numărul de elemente pe care le are acesta. Într-un panel, definim eticheta în cauză:

<mx:Panel title="Tutorial II – DataBinding" width="50%" height="50%">

<mx:Label text="Numărul de elemente definite : {myAC.length}" />

</mx:Panel>

Putem observa cu uşurinţă că pe lângă binding-ul definit de noi între proprietatea length a ArrayCollection-ului, eticheta conţine şi text – proprietatea text a etichetei este compusă din concatenarea rezultatului de binding cu o valoare statică, introdusă de noi, rezultatul rulării aplicaţiei fiind eticheta cu textul „Numărul de elemente definite : 5”.

În acelaşi proiect, vom defini o a doua legătură, utilizând maniera tag MXML : între o nouă etichetă şi primul element al ArrayCollection-ului.

Noua etichetă (definită în tag-ul panel-ului):

<mx:Label id="myLabelDest" text="test" />

şi legătura, definită în tag-ul aplicaţiei (dacă am defini-o în tag-ul panel-ului, ar rezulta eroare):

<mx:Binding source="myAC.getItemAt(0).siteURL" destination="myLabelDest.text"/>

Aşa cum puteţi găsi şi în documentaţie, metoda getItemAt întoarce elementul al cărui index este indicat drept parametru, în cazul nostru fiind vorba despre primul element (numărătoarea elementelor în array-uri începe de la 0). Binding-ul citeşte proprietatea siteURL a primului element al ArrayCollection-ului şi îl leagă de proprietatea text a etichetei nou definite – cu riscul de a repeta, modificarea proprietăţii siteURL a elementului în cauză, atrage după sine modificarea conţinutului proprietăţii text a etichetei.

 

Pentru a ne folosi şi de cea de a treia manieră de a crea legături, vom insera cod ActionScript în proiectul nostru, după cum urmează:

 

<mx:Script>

<![CDATA[

 

import mx.binding.utils.BindingUtils;

 

public function facLegatura ():void{

BindingUtils.bindProperty(my_TI, "text", myAC.getItemAt(0), "siteNume");

}

 

]]>

</mx:Script>

 

Bineînţeles, este necesar să adaugăm în cadrul Panel-ului şi următoarele tag-uri, la care codul ActionScript face referire:

 

<mx:TextInput id="my_TI" text=""/>

<mx:Button label="Legătură" click="facLegatura()" />

 

În linii mari, în momentul în care vom face click pe butonul cu eticheta „Legătură”, invocarea metodei facLegatura crează legătura dintre proprietatea siteNume a primului element al ArrayCollection-ului şi proprietatea text a noului TextInput definit. Puteţi observa că în maniera tag <mx:Binding> am făcut legătura accesând direct proprietatea primului element, pe când în cazul legăturii folosind ActionScript, am indicat sursa ca fiind primul element, indicând conform sintaxei, imediat după sursă proprietatea la care facem referire – siteNume.

 

În concluzie, puteţi observa cu uşurinţă că maniera de declarare folosind <mx:Binding> poate ajuta la separarea clară între interfaţă şi nivelul model (cel care se ocupă cu declararea şi procesarea datelor) şi poate crea legături între diverse surse şi aceeaşi destinaţie. Utilizarea clasei BindingUtils ajută la crearea şi gestionarea de legături, dar care poate fi aplicată oriunde într-un proiect complex.

Maniera pe care o veţi folosi va depinde întotdeauna de situaţie: reţineţi că atunci când urmăriţi obţinerea unui rezultat care întârzie să apară (spre exemplu, aveţi în aplicaţie o eroare pe care n-o puteţi depăşi uşor) puteţi schimba oricând maniera de realizare a legăturilor - data binding-ul a fost creat ca să vă ajute, nu să vă încurce.

Feb17th2008

Tutorial I : Componente Flex şi cod ActionScript

Platforma Flex este bazată pe funcţiile player-ului Flash 9 care gestionează aplicaţiile pe partea de client (ca o explicaţie suplimentară despre Flex), totodată furnizând interacţiunea cu JavaScript şi conţinut HTML. Faptul că player-ul Flash s-a impus foarte mult, la care se adaugă independenţa lui de platformă, ajută programatorul să economisească timp şi resurse necesare la testare şi debugging.

Aplicaţiile Flex sunt create utilizând două tipuri de fişiere: MXML şi ActionScript 3. Fişierele MXML sunt bazate pe standardul impus de XML, definind layout-ul interfeţei prin utilizarea tag-urilor, în special a celor care corespund elementelor vizibile (containere şi controale), dar şi a celor nereprezentate vizual: date, data binding (păstrarea legăturii între o componentă şi datele pe care aceasta le foloseşte) şi resurse în legătură cu serverul. Fişierele ActionScript 3 sunt scrise într-un limbaj de programare asemănător lui JavaScript (de altfel, există voci care afirmă că ActionScript 3 este, în fapt, JavaScript 2). De reţinut aspectul că limbajul ActionScript 3 este case sensitiv (variabilele, cuvintele cheie, clasele, metodele şi proprietăţile au nume diferite distinctiv de litere mari sau mici : myCombo nu este echivalent cu MYCoMBo).

Modelul Adobe - succesul tehnologiei acesteia - se bazează pe un set consistent de componente ce pot fi utilizate. Fiecărei componente îi corespunde un tag, iar fiecare tag este descris în framework printr-o clasă (descrisă bineinţeles într-un fişier ActionScript şi supunându-se regulilor de programare orientată pe obiecte). Folosindu-ne de conceptele de moştenire şi polimorfism aplicabile, putem extinde şi personaliza componentele în funcţie de necesităţile aplicaţiei pe care o construim.

Pentru a extinde o componentă Flex este suficient să dezvoltăm o subclasă a respectivei componente, (re)definind metodele şi proprietăţile de care avem nevoie pentru a obţine rezultatul dorit. Este bine de ştiut că toate containerele şi controalele Flex îşi moştenesc proprietăţile din clasa UIComponent, lucru de care ne putem folosi oricând ştiind ce anume oferă moştenitorilor această clasă.

În tutorialul de faţă, vom extinde componenta (controlul) ComboBox (mx.controls.ComboBox), presupunând că aţi trecut deja de aplicaţia Hello World şi că aveţi cunoştinţele minime necesare pentru a urmări logica şi a vă completa cunoştinţele.

Sintaxa generală pentru a extinde o componentă utilizând ActionScript 3, este "public class <clasă extinsă> extends <clasa moştenită>", unde clasa extinsă şi clasa moştenită se înlocuiesc după necesităţi cu numele claselor în cauză, în exemplul nostru public class cmbJudete extends ComboBox. Fişierul care conţine codul clasei extinse se va numi cmbJudete (atenţie, case-sensitive) la care se adaugă extensia .as, după caz .MXML - numele şi poziţia acestui fişier în sistemul de directoare al proiectului aplicaţiei modifică condiţiile din explicaţia referitoare la namespace.

Este de la sine înţeles că puteţi extinde componente şi utilizând fişiere MXML, diferenţa constând în maniera de a descrie metodele şi proprietăţile - deşi, în ultimă instanţă, rezultatul este acelaşi (făcându-se referire la acelaşi tip de meta-data, data-binding sau alte accesorii).

Pentru a putea folosi componenta extinsă creată, este necesar ca în cadrul aplicaţiei noastre, să declarăm namespace-ul respectivei componente, înainte de a o folosi - astfel încât să fuznizăm informaţii compilatorului despre locaţia în care se găseşte fişierul sursă. Cu siguranţă, aţi observat că toate aplicaţiile au cel puţin o declaraţie namespace <mx : Application xmlns : mx = "http://www.adobe.com/2006/mxml" /> care indică sursa (inclusiv natura sursei - dacă e text sau precompilată, ca în cazul componentelor native), ecosistemul componentelor Flex fiind astfel precedate de particula mx definită în tag-ul aplicaţiei.

Primul pas pe care-l vom face este acela de a defini clasa ActionScript care descrie componenta noastră:

  1. într-un proiect Flex nou, adăugăm subdirectorul ro, în care creăm subdirectorul flexug, în care creăm subdirectorul tutorial1.
  2. dacă folosiţi Flex Builder, selectaţi File » New » ActionScript Class. În vrăjitorul apărut, denumiţi pachetul (package) ro.flexug.tutorial1 şi clasa cmbJudete.as; indicaţi superclass ca fiind mx.controls.ComboBox
  3. codul generat de vrăjitor ar trebui să arate aşa:
Actionscript:
  1. package ro.flexug.tutorial1{
  2.  
  3. import mx.controls.ComboBox;
  4. public class cmbJudete extends ComboBox{
  5. public function cmbJudete():void{
  6. }
  7. }
  8. }

După cum bănuiţi, vrăjitorul a pregătit terenul pentru noi, declarând ce importăm, ce clasă moştenim şi a scris codul constructorului, clasa fiind declarată publică, astfel încât s-o putem folosi utilizând un tag MXML. Constructorul este metoda principală a unei clase, fiind prima metodă chemată imediat ce obiectul a fost creat. Particularitatea unui constructor constă în aceea că poartă acelaşi nume cu clasa pe care o "construieşte", nu poate întoarce valori (void întotdeauna) şi nu este moştenită.

Scopul nostru este să creăm un ComboBox modificat, care să conţină date: definim trei proprietăţi care conţin localităţi din judeţele Galaţi, Botoşani şi rezultatul concatenarii acestora. Aceste trei array-uri vor fi dataProvider pentru ComboBox-ul nostru, iar în faza de iniţializare, utilizatorul va decide care dintre valori să fie folosite. Pentru modificarea dataProvider-ului în funcţie de indicarea valorii, vom defini o metodă care stabileşte acest lucru în funcţie de un parametru de tip string (care poate lua valorile: toate, galati, botosani). Metadata-ul [Inspectable] este folosit pentru a descrie atributul ce schimba valorile dataProvider-ului. Acest metadata poate accepta mai mulţi parametri, printre care valoare implicită folosită în cazul getter/setter-ului, respectiv enumerarea valorilor pe care le poate lua acest atribut.

Metodele de tip getter/setter sunt modalităţi care permit schimbarea valorii unei proprietăţi private. Aceste metode sunt accesibile în afara clasei şi pot fi accesate prin numele proprietăţii, oferind posibilitatea de a crea proprietăţi cu funcţionalitate sofisticată, spre deosebire de proprietăţile publice clasice, care se pot accesa direct. Practic, dacă instanţa clasei s-ar numi jCmb, atunci jCmb.alegeJudet = 'toate' cheamă metoda care operează cu dataProvider-ul, totodată păstrând valoarea 'toate' în proprietatea privată a clasei numită 'judet'.

În urma celor prezentate, codul clasei noastre va arăta aşa:

Actionscript:
  1. package ro.flexug.tutorial1{
  2. import mx.controls.ComboBox;
  3.  
  4. public class cmbJudete extends ComboBox{
  5.  
  6. private var galati:Array = new Array("Galaţi","Barboşi","Tecuci", "Târgu Bujor", "Cuca");
  7. private var botosani:Array = new Array("Botoşani", "Dorohoi","Darabani" , "Brăieşti" , "Cucuteni","Dumeni" , "Broscăuţi" , "Leorda", "Rădăuţi-Prut", "Roma");
  8.  
  9. private var toateLocurile:Array = galati.concat(botosani);
  10.  
  11. [Inspectable(defaultValue="toate",enumeration=galati, botosani, toate)] private var judet:String = "toate";
  12.  
  13. public function cmbJudete():void{
  14. }
  15.  
  16. public function set alegeJudet(judetParam:String):void{
  17. judet = judetParam;
  18. if (judet == "galati"){
  19. this.dataProvider = galati;
  20. }else if (judet == "botosani" ){
  21. this.dataProvider = botosani;
  22. }else{
  23. this.dataProvider = toateLocurile;
  24. }
  25. }
  26.  
  27. [Bindable]
  28. public function get alegeJudet():String{
  29. return judet;
  30. }
  31.  
  32. }//sfarsit clasa
  33. } //sfarsit pachet

Componenta noastră este definită, dar este necesar să declarăm namespace-ul acesteia în cadrul aplicaţiei înainte de a o putea folosi:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:comp="ro.flexug.tutorial1.*">
</mx:Application>

După cum puteţi observa, namespace-ul indică structura de directoare anterior definită, astfel încât compilatorul să poată identifica sursa cu uşurinţă. Locaţia se închiei cu *, ceea ce indică ca puteţi defini şi alte componente în acelaşi director, cu condiţia să păstraţi acelaşi pachet - package (ro.flexug.tutorial1). Mai jos, puteţi observa utilizarea namespace-ului definit, <comp:cmbJudete id="jCmb" alegeJudet="toate" />

<mx:Application mlns:mx="http://www.adobe.com/2006/mxml" xmlns:comp="ro.flexug.tutorial1.*">
<mx:Panel title="Tutorial 1 - Componente si ActionScript" width="300" height="200">
<comp:cmbJudete id="jCmb" alegeJudet="toate" />
</mx:Panel>
</mx:Application>

Puteţi testa aplicaţia, sfârşind astfel walk through-ul al acestui tutorial.

Cu siguranţă vă veţi dori să extindenţi şi mai mult componenta creată. Iată o idee:

  1. în aplicaţie, adăugăm un grup de butoane radio, prin care să se facă selecţia dataProvider-ului componentei:
  2. <mx:RadioButtonGroup id="rgrup" itemClick="jCmb.schimbaJudet(event.currentTarget.selectedValue)"/>
    <mx:RadioButton groupName="rgrup" id="toate" value="toate" label="Toate" width="150"/>
    <mx:RadioButton groupName="rgrup" id="galati" value="galati" label="Galaţi" width="150"/>
    <mx:RadioButton groupName="rgrup" id="botosani" value="botosani" label="Botoşani" width="150"/>

  3. după cum puteţi observa, schimbaJudet nu este definit în componenta noastră. Mai mult decât atât, vorbim deja de evenimente - ca o introducere pentru următorul tutorial.
  4. Actionscript:
    1. [Bindable(event="schimbaJudetEvt")]
    2. public function schimbaJudet(judetParam:String):String{
    3. judet = judetParam;
    4. if (judet == "galati"){
    5. this.dataProvider = galati;
    6. }else if (judet == "botosani" ){
    7. this.dataProvider = botosani;
    8. }else{
    9. this.dataProvider = toateLocurile;}
    10. dispatchEvent(new Event("schimbaJudetEvt"));
    11. return judet;
    12. }

  5. în setter-ul lui alegeJudet se adaugă la sfârşitul acesteia, anunţarea evenimentului (schimbarea de dataProvider implicită, pentru o funcţionare corectă - corelare între componente) : dispatchEvent(new Event("schimbaJudetEvt"));
  6. de asemenea, getter-ul lui alegeJudet trebuie să dispună anunţarea evenimentului, prin adăugarea înainte de corpul metodei a [Bindable(event="schimbaJudetEvt")] - din acelaşi motiv de corelare între componente.
  7. se poate adăuga în cadrul Panel-ului şi eticheta <mx:Label text="Localităţile judeţului {jCmb.alegeJudet} sunt încărcate"/> pentru a verifica care dintre valori sunt încărcate şi a testa interacţiunea dintre componente.

Deşi aceste din urmă explicaţii fac referire la termeni neexplicaţi în cadrul acestui tutorial, cum sunt cei legaţi de evenimente şi de data - binding, ele sunt prezente aici pentru a vă deschide apetitul pentru mai multe noţiuni şi pentru a pregăti terenul următoarelor tutoriale.