È molto comune caricare dati esterni (come i file SWF) durante il runtime, ma solo quando i dati sono completamente caricati possiamo leggere o manipolare il suo contenuto. Di solito dobbiamo ascoltare l'evento completo inviato da a caricatore o URLLoader oggetto che carica i dati per la gestione del completamento. Spesso, scriviamo il codice che carica i dati in una funzione e scriviamo il codice che gestisce il completamento del caricamento in un'altra funzione, ma questo può essere migliorato raggruppando l'intero processo di caricamento insieme ...
Questo tutorial dimostra come creare un'estensione di caricamento nel framework dei comandi nel mio tutorial precedente, Thinking in Commands, parte 1 di 2, per impacchettare il caricamento della gestione del completamento in un unico punto. Questa estensione di caricamento può anche essere combinata con il framework di gestione delle scene trattato in Thinking in Commands parte 2 di 2. Molte classi utilizzate in questo tutorial sono trattate nel tutorial precedente, quindi consiglio vivamente di leggere le esercitazioni precedenti prima di andare avanti.
Inoltre, questo tutorial introduce il concetto di data manager, una "banca" centrale che memorizza i riferimenti agli oggetti dati. È possibile registrare i dati nella gestione dei dati con una stringa di chiavi univoca e successivamente accedere ai dati fornendo la stringa di chiavi corrispondente. Questo ti risparmia la fatica di conservare riferimenti di oggetti dati e alcuni problemi di ambito variabile.
A proposito, avrai bisogno della piattaforma Tweening GreenSock per completare questi esempi.
Normalmente, gestiamo i dati caricati all'interno della funzione listener di eventi completa. Questo rompe due blocchi di codice che sono collegati logicamente. E guardando il codice, il tuo flusso di pensiero potrebbe essere interrotto mentre la tua vista salta dalla funzione di caricamento all'intero listener di eventi.
Diamo un'occhiata al flusso logico di un ingenuo approccio di caricamento SWF.
Il caricatore carica un SWF da un URL e il onComplete () la funzione è invocata dal dispatchEvent () metodo che invia un evento completo, in cui il dispatchEvent () il metodo è invocato internamente dal loader. Beh, in realtà, è invocato dal LoaderInfo oggetto che appartiene al caricatore oggetto, ma per semplicità, diciamo solo il dispatchEvent () il metodo è invocato dal programma di caricamento.
Successivamente, all'interno del onComplete () funzione, il doMoreStuff () la funzione viene invocata dopo aver completato la gestione del caricamento e, come suggerisce il nome della funzione, fa più cose.
Il flusso logico di alto livello è molto lineare: invoca il Loader.load () prima il metodo, onComplete () secondo, e doMoreStuff () terzo. Tuttavia, come noterete dal diagramma, l'invocazione di ciascuna funzione è incorporata nel corpo della funzione della precedente, risultando in un codice "annidato". Secondo la mia opinione, se il flusso logico di una determinata funzionalità è lineare, il codice associato dovrebbe essere scritto in modo lineare, non annidato. Altrimenti, il codice potrebbe a volte diventare confuso se il livello del nido di invocazione è troppo alto.
Questo è quando l'approccio Comando entra in gioco. Dal diagramma sottostante, possiamo vedere che il codice è piuttosto lineare usando i comandi, in quanto tutti i comandi sono concatenati linearmente da un comando seriale. Anche se il programma "devia" nel setProperties (), addChildLoader (), e doMoreStuff () funzioni; la loro invocazione è lineare.
Bene, prima di iniziare a leggere qualcosa sul caricamento, diamo prima un'occhiata al Gestore dati classe. Un gestore dati consente di associare una stringa di chiavi con un oggetto dati ed è possibile ottenere un riferimento a questo oggetto dati ovunque nel codice. Con il gestore dati, non devi preoccuparti di conservare i riferimenti ai dati e gli ambiti variabili. Tutto quello che devi fare è registrare un dato al manager con una stringa di chiavi.
La codifica è piuttosto semplice, come mostrato di seguito:
dati del pacchetto import flash.utils.Dictionary; public class DataManager // un dizionario che mantiene le relazioni stringa-dati private static var _data: Dictionary = new Dictionary (); // restituisce l'oggetto dati associato a una stringa chiave public static function getData (key: String): * return _data [key]; // registra un oggetto dati con una stringa di chiavi public static function registerData (key: String, data: *): void _data [key] = data; // annulla la registrazione di una stringa chiave public static function unregisterData (key: String): void delete _data [key]; // annulla la registrazione di tutte le stringhe di chiavi public static function clearData (): void for (var key: String in _data) delete _data [key];
Quindi, quando vogliamo registrare una stringa di chiavi "myData" con un oggetto dati, ad esempio uno sprite, potremmo scrivere il codice come segue:
var sprite: Sprite = new Sprite (); DataManager.registerData ("myData", sprite);
Successivamente, ovunque nel codice, potremmo scrivere il seguente codice per ottenere un riferimento dello sprite e aggiungerlo a un elenco di visualizzazione. È così semplice, non ci sono più problemi riguardo al mantenimento dei riferimenti agli oggetti e degli ambiti variabili.
var sprite: Sprite = DataManager. getData ("myData") come Sprite; container.addChild (sprite);
Ora diamo un'occhiata a come l'approccio di caricamento ingenuo carica un'immagine esterna. Il codice di caricamento si trova in una funzione e il codice di gestione del completamento si trova in un altro. Caricheremo tre immagini e le aggiungeremo sul palco al termine del caricamento. Inoltre, controlleremo il progresso del caricamento ascoltando gli eventi di avanzamento.
Apri Flash e crea un nuovo documento Flash.
Disegna una barra di avanzamento sul palco; questo è per rappresentare il progresso del caricamento. Converti l'intera barra di avanzamento in un simbolo e assegnagli il nome di un'istanza di "progressBar_mc". All'interno del simbolo della barra di avanzamento, converti la barra di avanzamento interna in un altro simbolo e assegnagli un nome di istanza di "innerBar_mc".
Inserire tre immagini nella stessa cartella del file FLA, denominate "image1.jpg", "image2.jpg" e "image3.jpg". Ecco come appaiono le tre immagini.
Creare un nuovo file AS per la classe di documento per il file FLA. Il codice è piuttosto semplice e tutti i dettagli sono spiegati nei commenti. Per prima cosa vengono creati tre caricatori e inizia il caricamento. Su ogni evento di progresso, la barra di avanzamento viene aggiornata. Una volta completato il caricamento, la barra di avanzamento si attenua e le tre immagini si dissolvono una per una.
package import com.greensock.TweenMax; import flash.display.DisplayObject; import flash.display.Loader; import flash.display.MovieClip; import flash.events.Event; import flash.events.ProgressEvent; import flash.net.URLRequest; public class NaiveLoading estende MovieClip private var loader1: Loader; private var loader2: Loader; private var loader3: Loader; funzione pubblica NaiveLoading () // shrink progress bar to zero scale progressBar_mc.innerBar_mc.scaleX = 0; // crea caricatori loader1 = new Loader (); loader2 = nuovo Loader (); loader3 = nuovo Loader (); // aggiungi progress ascoltatori loader1.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, onProgress); loader2.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, onProgress); loader3.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, onProgress); // aggiungi listener di completamento loader1.contentLoaderInfo.addEventListener (Event.COMPLETE, onComplete); loader2.contentLoaderInfo.addEventListener (Event.COMPLETE, onComplete); loader3.contentLoaderInfo.addEventListener (Event.COMPLETE, onComplete); // inizia a caricare loader1.load (new URLRequest ("image1.jpg")); loader2.load (new URLRequest ("image2.jpg")); loader3.load (new URLRequest ("image3.jpg")); private function onProgress (e: ProgressEvent): void // calcola i bit totali per caricare var bytesTotal: uint = 0; bytesTotal + = loader1.contentLoaderInfo.bytesTotal; bytesTotal + = loader2.contentLoaderInfo.bytesTotal; bytesTotal + = loader3.contentLoaderInfo.bytesTotal; // calcola i bit totali caricati var bytesLoaded: uint = 0; bytesLoaded + = loader1.contentLoaderInfo.bytesLoaded; bytesLoaded + = loader2.contentLoaderInfo.bytesLoaded; bytesLoaded + = loader3.contentLoaderInfo.bytesLoaded; // aggiorna la barra della progress progressBar_mc.innerBar_mc.scaleX = bytesLoaded / bytesTotal; private var _completeCount: int = 0; funzione privata onComplete (e: Event): void _completeCount ++; if (_completeCount < 3) return; //remove progress listeners loader1.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress); loader2.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress); loader3.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress); //remove completion listeners loader1.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete); loader2.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete); loader3.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete); var image1:DisplayObject = loader1.content; var image2:DisplayObject = loader2.content; var image3:DisplayObject = loader3.content; //adjust loaded image positions image1.x = 30, image1.y = 30; image2.x = 230, image2.y = 30; image3.x = 430, image3.y = 30; //add loaded images to display list addChild(image1); addChild(image2); addChild(image3); //fade out progress bar TweenMax.to(progressBar_mc, 0.5, autoAlpha:0, blurFilter:blurX:20, blurY:20); //fade in loaded images TweenMax.from(image1, 0.5, delay:0.5, alpha:0, blurFilter:blurX:20, blurY:20); TweenMax.from(image2, 0.5, delay:0.7, alpha:0, blurFilter:blurX:20, blurY:20); TweenMax.from(image3, 0.5, delay:0.9, alpha:0, blurFilter:blurX:20, blurY:20);
Premere CTRL + INVIO per testare il film. Vedrai che la barra di avanzamento scompare immediatamente e le tre immagini si dissolvono. Ciò accade perché le immagini sono file locali, il che significa che possono essere caricati quasi immediatamente. Per simulare la velocità di download online, selezionare prima Visualizza> Scarica impostazioni> DSL come velocità di download simulata, quindi premere di nuovo CTRL + INVIO senza chiudere la finestra di test per avviare la simulazione del download in linea. Questa volta vedrai i progressi crescere progressivamente prima che svanisca.
Va bene, è il momento di caricare le immagini con il quadro dei comandi.
Prima di procedere, creiamo alcuni comandi di utilità che verranno utilizzati in seguito nell'esempio. Ancora una volta, queste classi di comando sono basate sul framework dei comandi presentato nel mio precedente tutorial (Parte 1) e consiglio vivamente di esaminarle prima di procedere. Se hai già letto il tutorial in precedenza, puoi sempre tornare indietro se hai bisogno di aggiornare la memoria.
Qui creeremo due comandi per la registrazione e l'annullamento della registrazione dei dati per la classe data manager. Il RegisterData comando registra i dati nel gestore, mentre il UnregisterData comando annulla la registrazione dei dati.
pacchetto commands.data import commands.Command; import data.DataManager; // questo comando registra i dati nella classe pubblica data manager RegisterData estende Command public var key: String; dati var pubblici: *; funzione pubblica RegisterData (chiave: String, data: *) this.key = key; this.data = data; override protected function execute (): void DataManager.registerData (chiave, dati); completare();
pacchetto commands.data import commands.Command; import data.DataManager; // questo comando annulla la registrazione dei dati dalla classe pubblica data manager UnregisterData estende Command public var key: String; funzione pubblica UnregisterData (key: String) this.key = key; override protected function execute (): void DataManager.unregisterData (chiave); completare();
Questo comando incapsula a caricatore esempio di caricare() metodo. Puoi fornire un onProgress comando che viene eseguito su ogni evento progress e an onComplete eseguito quando il caricamento è completo. Si noti che il completare() il metodo viene richiamato al termine del caricamento. Questa linea di codice è estremamente cruciale. Se non si richiama il metodo, il comando non sarà mai considerato completo, bloccando l'intera applicazione nello scenario peggiore.
pacchetto commands.loading import commands.Command; import flash.display.Loader; import flash.events.Event; import flash.events.ProgressEvent; import flash.net.URLRequest; import flash.system.LoaderContext; public class LoaderLoad estende Command public var loader: Loader; url var pubblico: URLRequest; contesto var pubblico: LoaderContext; public var onProgress: Command; public var onComplete: Command; funzione pubblica LoaderLoad (loader: Loader, url: URLRequest, contesto: LoaderContext = null, onProgress: Command = null, onComplete: Command = null) this.loader = loader; this.url = url; this.context = context; this.onProgress = onProgress; this.onComplete = onComplete; override protected function execute (): void // aggiungi listener loader.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, progressListener); loader.contentLoaderInfo.addEventListener (Event.COMPLETE, completeListener); loader.contentLoaderInfo.addEventListener (Event.COMPLETE, loadingComplete); // inizia a caricare loader.load (url, context); private function loadingComplete (e: Event): void // remove listeners loader.contentLoaderInfo.removeEventListener (ProgressEvent.PROGRESS, progressListener); loader.contentLoaderInfo.removeEventListener (Event.COMPLETE, completeListener); loader.contentLoaderInfo.removeEventListener (Event.COMPLETE, loadingComplete); completare(); funzione privata progressListener (e: ProgressEvent): void // esegue il comando onProgress if (onProgress) onProgress.start (); private function completeListener (e: Event): void // esegue il comando onComplete if (onComplete) onComplete.start ();
Questo comando incapsula l'invocazione di un'altra funzione. È progettato per consentire all'utente di fornire un array di parametri aggiuntivo per la funzione da invocare.
pacchetto commands.utils import commands.Command; // questo comando richiama una funzione public class InvokeFunction estende Command public var func: Function; public var args: Array; funzione pubblica InvokeFunction (func: Function, args: Array = null) this.func = func; this.args = args; override function protected execute (): void func.apply (null, args); completare();
Questo è tutto. Tempo per l'esempio.
Copia il file FLA dall'esempio precedente in una nuova cartella e copia i file immagine insieme a esso.
Creare un nuovo file AS per la classe di documento del file FLA copiato, denominato "LoadingDataWithCommands". Ricordarsi di cambiare il nome della classe del documento nel file FLA in questo nuovo.
Il codice per la classe del documento è abbastanza pulito. Imposta semplicemente la scena corrente su a LoadingScene con un manager di scena. Stiamo usando il framework scene presentato nel mio precedente tutorial (Parte 2). Puoi verificarlo se hai dimenticato come usarlo.
package import flash.display.MovieClip; import scene.SceneManager; public class LoadingDataWithCommands estende MovieClip public function LoadingDataWithCommands () var sceneManager: SceneManager = new SceneManager (); sceneManager.setScene (new LoadingScene (this));
Ci sono due scene in totale. Il LoadingScene carica le immagini e aggiorna la barra di avanzamento. Al termine del caricamento, la scena passa a MainScene, che sfuma le immagini caricate.
Estendere la Scena classe per creare una nuova classe chiamata LoadingScene. Il contenitore la proprietà contiene un riferimento allo sprite principale. Questo ci consente di accedere alla barra di avanzamento in seguito.
pacchetto scene import.Scene; public class LoadingScene estende Scene container var privato: LoadingDataWithCommands; funzione pubblica LoadingScene (container: LoadingDataWithCommands) this.container = container;
Ora, crea il comando di introduzione per la scena di caricamento. L'introduzione creerà tre caricatori e inizierà il processo di caricamento. Questo viene fatto ignorando il createIntroCommand () metodo. Il seguente codice entra nel corpo della classe, come il costruttore.
// il comando di introduzione inizia il caricamento delle tre immagini sovrascrive la funzione pubblica createIntroCommand (): Command var loader1: Loader = new Loader (); var loader2: Loader = new Loader (); var loader3: Loader = new Loader (); comando var: Comando = new ParallelCommand (0, // riduce la barra di avanzamento a zero nuove SetProperties (container.progressBar_mc.innerBar_mc, scaleX: 0), // comandi relativi al caricamento eseguiti in serie nuovo SerialCommand (0, / / registra i tre caricatori sul nuovo gestore dati di ParallelCommand (0, nuovo RegisterData ("loader1", loader1), nuovo RegisterData ("loader2", loader2), nuovo RegisterData ("loader3", loader3)), // avvio tre caricamento comandi in parallelo new ParallelCommand (0, new LoaderLoad (loader1, new URLRequest ("image1.jpg"), null, new InvokeFunction (onProgress) // comando OnProgress), nuovo LoaderLoad (loader2, new URLRequest ("image2.jpg") , null, nuovo comando InvokeFunction (onProgress) // onProgress), nuovo LoaderLoad (loader3, new URLRequest ("image3.jpg"), null, nuovo comando InvokeFunction (onProgress) // onProgress)))); comando di ritorno;
Quindi, ignora il onSceneSet () metodo. Questo metodo viene invocato quando il comando di introduzione è completo, a indicare che il caricamento è completo. All'interno di questo metodo, diciamo al responsabile della scena di passare alla scena principale. Prima della transizione della scena, il comando outro viene eseguito per primo.
override public function onSceneSet (): void sceneManager.setScene (new MainScene (container));
E poi scavalcare il createOutroCommand. Questo comando svanirà la barra di avanzamento.
// il comando outro svanisce la barra di avanzamento sovrascrive la funzione pubblica createOutroCommand (): Comando var command: Command = new SerialCommand (0, // svanisce la barra di avanzamento new TweenMaxTo (container.progressBar_mc, 0.5, autoAlpha: 0, blurFilter : blurX: 20, blurY: 20), // rimuove la barra di avanzamento dall'elenco di visualizzazione new RemoveChild (container, container.progressBar_mc)); comando di ritorno;
Infine, crea il onProgress metodo invocato dal InvokeFunction comandi.
funzione privata onProgress (): void // richiama i riferimenti del caricatore dal gestore dati var loader1: Loader = DataManager.getData ("loader1") come Loader; var loader2: Loader = DataManager.getData ("loader2") come Loader; var loader3: Loader = DataManager.getData ("loader3") come Loader; // calcola i bit totali per caricare var bytesTotal: uint = 0; bytesTotal + = loader1.contentLoaderInfo.bytesTotal; bytesTotal + = loader2.contentLoaderInfo.bytesTotal; bytesTotal + = loader3.contentLoaderInfo.bytesTotal; // calcola i bit totali caricati var bytesLoaded: uint = 0; bytesLoaded + = loader1.contentLoaderInfo.bytesLoaded; bytesLoaded + = loader2.contentLoaderInfo.bytesLoaded; bytesLoaded + = loader3.contentLoaderInfo.bytesLoaded; // aggiorna la barra della barra di avanzamento container.progressBar_mc.innerBar_mc.scaleX = bytesLoaded / bytesTotal;
Ora crea una nuova classe per la scena principale, estendendo il Scena classe.
pacchetto scene import.Scene; public class MainScene estende Scene container var privato: LoadingDataWithCommands; funzione pubblica MainScene (container: LoadingDataWithCommands) this.container = container;
Ignora il createIntroCommand () metodo. Questo metodo aggiungerà i caricatori all'elenco di visualizzazione e li dissolverà in uno a uno. Inoltre, le stringhe della chiave di dati non sono registrate dal gestore dati.
sovrascrive la funzione pubblica createIntroCommand (): Comando // richiama i riferimenti del caricatore dal gestore dati var loader1: Loader = DataManager.getData ("loader1") come Loader; var loader2: Loader = DataManager.getData ("loader2") come Loader; var loader3: Loader = DataManager.getData ("loader3") come Loader; var command: Command = new ParallelCommand (0, // loaded-image-handling commands new SerialCommand (0, // aggiusta le posizioni dell'immagine caricate new ParallelCommand (0, new SetProperties (loader1, x: 30, y: 30), nuovo SetProperties (loader2, x: 230, y: 30), nuovo SetProperties (loader3, x: 430, y: 30)), // aggiungi immagini caricate per visualizzare la lista new ParallelCommand (0, new AddChild (container , loader1), nuovo AddChild (contenitore, loader2), nuovo AddChild (contenitore, loader3)), // dissolvenza in immagini caricate nuovo ParallelCommand (0, nuovo TweenMaxFrom (loader1, 0.5, blurFilter: blurX: 20, blurY: 20 ), nuovo TweenMaxTo (loader1, 0.5, autoAlpha: 1), nuovo TweenMaxFrom (loader2, 0.5, delay: 0.2, alpha: 0, blurFilter: blurX: 20, blurY: 20), nuovo TweenMaxTo (loader2, 0.5, delay: 0.2, autoAlpha: 1), nuovo TweenMaxFrom (loader3, 0.5, delay: 0.4, alpha: 0, blurFilter: blurX: 20, blurY: 20), nuovo TweenMaxTo (loader3 , 0.5, delay: 0.4, autoAlpha: 1))), // dati unregsiter dal nuovo gestore dati ParallelCommand (0, nuovo UnregisterData ("loader1"), nuovo Unregi sterData ("loader2"), nuovo UnregisterData ("loader3"))); comando di ritorno;
Tutto apposto. Sono stati fatti! Prova il film e simula il download online. Vedrai lo stesso risultato dell'esempio precedente, ma questa volta è tutto fatto con il quadro comandi e il framework scene.
In questo tutorial, ti ho mostrato come caricare immagini esterne con il quadro dei comandi. Il LoaderLoad il comando può essere usato anche per caricare file SWF esterni. Inoltre, è possibile creare i propri comandi per caricare dati esterni diversi dalle immagini e dai file SWF, incapsulando il file URLLoader classe nei tuoi comandi.
Abbiamo scritto più codice nel secondo esempio rispetto al primo. Ricorda, lo scopo dell'utilizzo del quadro comandi e del framework scene non è quello di ottenere lo stesso risultato con meno codice, ma di gestire il codice in un approccio sistematico e modulare, semplificando la vita quando si tratta di manutenzione e modifica future.
Il primo esempio comprime tutto il codice in una singola classe, rendendo difficile la manutenzione futura se la quantità di codice dovesse crescere molto. Il secondo esempio, d'altra parte, separa il codice logicamente indipendente in scene diverse, rendendo più facile la modifica futura. Inoltre, integrando il framework dei comandi e il framework delle scene, abbiamo lasciato spazio a future estensioni, dove possiamo aggiungere più scene e comandi di introduzione / uscita senza interrompere il codice irrilevante.
!