Adobe Air: aria di cambiamento
Adobe Air è finalmente arrivato. Dopo un solo anno di beta testing il più giovane prodotto di Adobe arriva ufficialmente tra noi. Sembra ieri quando un anno fa è stata annunciata la prima versione alpha in test pubblico con nome in codice Apollo. Da allora è stata un’evoluzione continua: abbiamo avuto due release Alpha, tre Beta e integrazione con i principali strumenti di sviluppo forniti dalla Casa delle Idee ( Flash, Flex e Dreamweaver ). Adobe Air in ogni caso non è solo una tecnologia legata a prodotti commerciali in quanto il compilatore è rilasciato sotto licenza open source, questo offre a chiunque la possibilità di sviluppare applicazioni in completa libertà. Altro elemento da non sottovalutare è la possibilità di scegliere tra diversi linguaggi di programmazione per lo sviluppo: Adobe Air infatti si rivolge non solo a sviluppatori Flex/Flash ma anche a professionisti che lavorano in ambito web utilizzando tecnologie più “tradizionali” quali HTML, Javascript e CSS.
Dopo questa dovuta introduzione ad una tecnologia che rivoluzionerà le vostre vite di sviluppatori affrontiamo la realizzazione della nostra prima applicazione Air; andremo a creare un semplice task manager utilizzando Flex e Actionscript.La nostra applicazione utilizzerà mxml e actionscript 3 per gestire una lista di attività da fare salvata in locale in un file XML; tramite l’interfaccia grafica sarà possibile visualizzare, modificare, aggiungere e rimuovere elementi alla nostra lista di attività. Essendo i dati salvati in formato XML sul disco sarà possibile utilizzare i dati in altre applicazioni senza complicazioni. Apriamo Flex 3 e creiamo un nuovo progetto desktop. (Immagine 1) Manteniamo le impostazioni di default offerte da Flex specificando che stiamo creando un’applicazione desktop. Verrà creato il file main.mxml che definirà l’aspetto della nostra applicazione. Per quanto concerne l’implementazione delle funzionalità utilizzeremo una classe actionscript che poi assoceremo al documento mxml; questa tecnica viene chiamata “code behind” e consiste nell’utilizzare il codice mxml solo per definire l’aspetto dell’applicazione e una classe esterna associata per controllare l’interazione.
Questa tecnica è molto efficace perchè permette una netta separazione tra aspetto e funzionalità, offrendo molta flessibilità nell’apportare modifiche al programma. Creiamo quindi la nostra classe esterna all’interno del namespace della nostra applicazione: cef62.demo.TaskList.as La nostra classe, essendo associata all’applicazione Air dovrà derivare dalla classe WindowedApllication. (Immagine 2) Una volta creata dovrebbe presentarsi così: (Immagine 3) ora siamo pronti per associare la nostra classe adibita a gestire la business logic al suo layout di presentazione, per fare questo dobbiamo aggiungere il namespace della nostra classe nella dichiarazione del nodo principale del documento mxml.
Dopo aver aggiunto il namespace possiamo finalmente definire l’associazione:
<!--<?xml version="1.0" encoding="utf-8"?> <demo:TaskList xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:demo="cef62.demo.*" layout="absolute" applicationComplete="init(event)" width="600" height="400" title="Demo Task List" backgroundColor="0xEEEEEE"> </demo:TaskList>
// variabile che verrà associata al file XML usato come sorgente dati protected var xmlDataFile:File; // istanza della classe stream, adibita alla lettura // e scrittura dei dati da e verso il file XML protected var stream:FileStream = new FileStream(); // XML ottenuto dal file esterno public var dpXml:XML; // dataprovider per la nostra datagrid, definito Bindable // così da poter essere collegato dinamicamente al componente visuale [Bindable] public var dp:XMLListCollection = new XMLListCollection(); // funzione di inizializzazione, chiamata dall'evento ApplicationComplete // generato una volta che il nostro layout mxml è pronto all'uso public function init( e:FlexEvent ):void { // carichiamo la sorgente di dati XML esterna updateDataSource(); } // funzione che carica il file XML e lo definisce // come dataprovider della datagrid private function updateDataSource():void { // carichiamo il file Xml xmlDataFile = File.desktopDirectory.resolvePath( "archivio.xml" ); dpXml = readFromXML(); // aggiorniamo il data provider // utilizzando i dati importati dal file XML esterno dp.source = dpXml[0].task; dp.refresh(); } // funzione che legge il contenuto di un file XML esterno // se il file non esiste inizializza l'applicazione come se // il file esterno esistesse e fosse vuoto private function readFromXML():XML { var tXML:XML; // verifichiamo se il file esiste if( xmlDataFile.exists ) { // oggetto bytearray per contenere contenuto binario del file letto var byteData:ByteArray = new ByteArray(); // apriamo stream al file stream.open( xmlDataFile, FileMode.READ ); // leggiamo il contenuto binario del file stream.readBytes( byteData, 0, xmlDataFile.size ); // chiudiamo lo stream al file stream.close(); // se il file è vuoto creiamo xml temp if(byteData.length > 0) { tXML = XML( byteData.readUTFBytes( byteData.length ) ); } else { tXML = <base> <task title="---" content="---" type="---"/> </base>; } } else { // se il file non esiste creiamo xml temp tXML = <base> <task title="---" content="---" type="---"/> </base>; } return tXML; }
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://ns.adobe.com/air/application/1.0.M6"> [...] <initialWindow> <systemChrome>none</systemChrome> <maximizable>false</maximizable> <resizable>false</resizable> <width>600</width> <height>400</height> </initialWindow> [...] </application>
<mx:DataGrid x="39" y="17" width="520" height="294" editable="true" id="dataGrid" dataProvider="{dp}" itemEditEnd="finishItemEditing(event)"> <mx:columns> <mx:DataGridColumn headerText="Titolo" dataField="@title"/> <mx:DataGridColumn headerText="Descrizione" dataField="@content"/> <mx:DataGridColumn headerText="Categoria" dataField="@type"/> </mx:columns> </mx:DataGrid>
// datagrid definita nel file mxml associato, マ necessario dichiararla come variabile // per renderla accessibile alle funzioni della nostra classe public var dataGrid:DataGrid; // funzione chiamata quando si conclude di modificare il contenuto // di una cella della datagrid // verifica se il contenuto マ stato modificato e se necessario // aggiorna il contenuto del file XML esterno public function finishItemEditing( e:DataGridEvent ):void { // datagrid di riferimento var ref:DataGrid = e.target as DataGrid; // XML dataprovider della riga interessata var xmlNodeRef:XML = XML( dp.getItemAt( e.rowIndex ) ); // contenuto della cella di testo editabile prima dell'azione di modifica var oldCellString:String = xmlNodeRef[ (ref.columns[ e.columnIndex ] as DataGridColumn ).dataField ]; // istanza del campo di testo modificato var itemEditorInstance:TextInput = TextInput( ref.itemEditorInstance ); // contenuto testuale dopo la modifica var newCellString:String = itemEditorInstance.text; // se necessario aggiorniamo il file XML esterno if( oldCellString != newCellString ) { writeXMLToFile(); } } // funzione che aggiorna il file XML esterno con i dati attualmente esistenti private function writeXMLToFile():void { // apriamo stream al file in modalitˆ sovrascrittura stream.open( xmlDataFile, FileMode.WRITE ); // scriviamo il contenuto testuale nel file stream.writeUTFBytes( createXMLStringToExport() ); // chiudiamo lo stream al file stream.close(); }
<mx:Button x="39" y="319" label="Aggiungi" click="addItem(event)"/> <mx:Button x="123" y="319" label="Elimina" click="removeItem(event)"/> <mx:Button x="458" y="319" label="view source.." click="showSource(event)"/>
si occuperanno di: aggiungere attività all’elenco, rimuovere l’attività selezionata dall’elenco e visualizzare in una nuova finestra la sorgente di dati XML. Questi tre pulsanti sono associati ad altrettante funzioni nella nostra classe Actionscript:
// chiamata quando si richiede l'aggiunta di un nuovo task // crea un nuovo task, in coda agli altri public function addItem( e:MouseEvent ):void { var lastItem:XML = XML( dp.getItemAt( dp.length -1 ) ); var newItem:XML = new XML( lastItem.toXMLString() ); newItem.@title = "---"; newItem.@content = "---"; newItem.@type = "---"; dp.addItem( newItem ); dp.refresh(); // aggiorniamo il file su disco writeXMLToFile(); } // chiamata quando si vuole eliminare il task selezionato // mostra un messaggio d'errore se non マ selezionata nessuna // riga della datagrid public function removeItem( e:MouseEvent ):void { var activeRow:int = dataGrid.selectedIndex; if( activeRow >= 0 && activeRow < dp.length ) { dp.removeItemAt( activeRow ); dp.refresh(); // aggiorniamo il file su disco writeXMLToFile(); } else { Alert.show( "Non è possibile eliminare nessuna riga, selezionarne una prima!", "Attenzione" ); } } // mostra il sorgente XML in una finestra independente // utilizzando le funzioni di gestione finestre di Adobe Air public function showSource( e:MouseEvent ):void { // creiamo una nuova finestra e ne definiamo l'aspetto var sourceView:Window = new Window(); sourceView.title = "Source View"; sourceView.maximizable = false; sourceView.minimizable = false; sourceView.resizable = false; sourceView.width = 650; sourceView.height = 500; sourceView.layout = "absolute"; // creiamo la textarea che visualizzerˆ il documento XML var txtArea:TextArea = new TextArea(); txtArea.x = 20; txtArea.y = 20; txtArea.width = 610; txtArea.height = 460; txtArea.editable = false; // definiamo un'indentazione leggibile per l'output a stringa da XML XML.prettyIndent = 2; XML.prettyPrinting = true; // assegnamo il testo XML all'area di testo txtArea.text = createXMLStringToExport(); // aggiungiamo l'area di testo alla finestra sourceView.addChild( txtArea ); // mostriamo la finestra sourceView.open(); } // funzione che genera una stringa XML valida utilizzando i dati presenti nella datagrid private function createXMLStringToExport():String { // dichiarazione XML + il separatore di linea corretto per il sistema operativo ospite var xmlDeclaration:String = '<?xml version="1.0" encoding="utf-8"?>' + File.lineEnding; // corpo XML var XMLString:String = "<base>" + dp.toXMLString() + "</base>"; var tXML:XML = new XML( XMLString ); // stringa XML completa var xmlExportString:String = xmlDeclaration + tXML.toXMLString(); return xmlExportString; }




