Eine "Preloader"-Anzeige für eigene Flex-Anwendungen
Mit Adobe Flex [1] erstellte
Anwendungen sind eigentlich nichts anderes als Flash [2]
"Movies" mit zwei "Frames" - und für Flash Movies hat Adobe vorgesehen,
daß mit dem Abspielen bereits begonnen werden kann, noch bevor die
gesamte Datei geladen wurde. Da Flex-Anwendungen meist mehrere 100kB
umfassen, empfiehlt sich der Einsatz eines "Preloader" mit einer
Anzeige, die den Benutzer über den aktuellen Ladefortschritt informiert.
Dieser Artikel beschreibt anhand eines konkreten Beispieles,
wie eine Anzeige für den Flex-Preloader erstellt und eingesetzt wird.
Die Klasse selbst sowie ein kleiner Demonstrator können
für alle unterstützten Plattformen heruntergeladen und nach Belieben in
eigenen
(kommerziellen wie nicht-kommerziellen) Projekten eingesetzt werden -
beide stehen unter der "MIT License" [3] mit
der zusätzlichen Einschränkung, daß jegliche Änderungen an dem vom
Autor herausgegebenen Original deutlich gekennzeichnet werden müssen
- dergestalt, daß der Autor nicht mit diesen Änderungen in Verbindung
gebracht werden kann!
Inhaltsübersicht
Um direkt zu einem bestimmten Abschnitt zu gelangen, klicken
Sie einfach auf die entsprechende Überschrift:
Preloader in Flex-Anwendungen
Flex-Anwendungen sind eigentlich nichts anderes als Flash
"Movies" mit zwei "Frames" [4]: das erste Bild (Frame)
enthält den Preloader, das zweite die eigentliche Flex-Anwendung [5]. Sobald das erste Frame vollständig geladen ist, kann
es angezeigt (d.h. der Preloader ausgeführt) werden. Sobald das zweite
Frame geladen ist, kann mit dem Ausführen der Anwendung begonnen werden
- die Zeit dazwischen sollte eine Preloader-Anzeige möglichst sinnvoll
überbrücken.
Glücklicherweise packt Flex alle für den Preloader (samt
Anzeige) benötigten Komponenten in das erste Frame - wenn man also
dafür sorgt, daß die Preloader-Anzeige nicht zuviele zusätzliche
Klassen erfordert [6], kann das erste Frame zügig
geladen werden. Auf diese Weise bleibt die Zeit vom Aufruf der
SWF-Datei bis zur ersten visuellen Rückmeldung durch den Preloader
möglichst kurz.
Noch eine Anmerkung: in der Literatur [z.B. 4,5,6] wird das Wort "Preloader" häufig
auch dann verwendet, wenn nur von der Anzeige des Ladefortschritts die
Rede ist. Dabei ist der eigentliche Preloader fest in Flex integriert
und kann vom Programmierer nicht verändert werden - nur die Anzeige
läßt sich durch eine Eigenentwicklung ersetzen. Unglücklicherweise ist
auch Adobe in seiner Nomenklatur nicht konsequent: in der
MXML-Direktive für die Flex-Anwendung muß die Anzeigeklasse als
"preloader"-Attribut angegeben werden, selbst wenn die verwendete
Klasse (korrekterweise) das Interface "IPreloaderDisplay" implementiert.
Diese Präzisierung ist mehr als nur eine Spitzfindigkeit - sie
ist wichtig für das Verständnis (und ggfs. die Erwartungshaltung des
Programmierers): anders als der wirkliche Preloader kann ein
"IPreloaderDisplay" den Ladevorgang nicht beeinflussen. Stattdessen
wird es vom Preloader instanziert und anschließend mit allen
erforderlichen Informationen (Größe und Aussehen der "stage" sowie
Ladefortschritt) versorgt. Die hier vorgestellte Klasse ist also nichts
mehr als ein "Sklave" des Preloader...
PreloaderDisplay - eine eigene
Fortschrittsanzeige
Nachdem eine Recherche keine passende Preloader-Anzeige
lieferte, blieb dem Autor nichts anderes übrig als selbst ein
"IPreloaderDisplay" zu entwickeln. Gefragt war eine schlichte, aber
aussagekräftige Anzeige mit einem ganz bestimmten Zeitverhalten:
- für Projekte mit kurzen Ladezeiten (< 0.5 Sekunden)
sollte gar keine Preloader-Anzeige erscheinen
- nach 0.5 Sekunden sollte nur dann eine Anzeige erscheinen,
falls die prognostizierte Ladezeit insgesamt über 1 Sekunde liegt;
- nach einer Sekunde sollte der Benutzer allerdings auf jeden
Fall eine visuelle Rückmeldung erhalten.
Außerdem sollte die Anzeige mindestens für zwei Sekunden auf
dem Bildschirm verbleiben - ein kurzes Aufflackern war (und ist) nicht
akzeptabel.
Das fertige Ergebnis sieht wie folgt aus:

Abb. Vorschau auf die Preloader-Anzeige des Autors
Die Anzeige ist einfach, enthält aber alle für den Betrachter
wichtigen Informationen - insbesondere auch eine Abschätzung der
verbleibenden Wartezeit.
Funktionsweise im Überblick
Ausgehend von den bereits genannten Beispielen (vor allem [5]) war die Entwicklung nicht sonderlich schwierig - die
komplette Logik konnte in eine einzige Klasse "PreloaderDisplay"
ausgelagert werden, die sich leicht in andere Projekte integrieren läßt.
Wie in [5] vorgeschlagen,
implementiert
"PreloaderDisplay" das Interface "IPreloaderDisplay", integriert aber
zugleich auch die tatsächliche Anzeige des Ladefortschrittes. Für die
Textanzeige
wurden (wie in [6] empfohlen)
"TextField"s verwendet (die nicht zum Flex Framework gehören, sondern
von Flash bereitgestellt werden), der eigentliche Ladebalken wird mit
Grafik-Befehlen direkt auf die Anzeige gezeichnet.
Der Ladefortschritt wird von Flash mitgeteilt, das
"PreloaderDisplay"
führt außerdem Buch über die (gemittelte) Ladegeschwindigkeit und
erechnet daraus eine Abschätzung für die noch ausstehende Ladezeit.
Wenn Sie ein Flash PlugIn (ab Version 9) installiert haben,
können Sie
sich die Optik des "PreloaderDisplay" vorführen lassen:
Vermutlich sehen Sie bereits den Text 'Click for a
demonstration of the "PreloaderDisplay"', da die verwendete SWF-Datei
längst geladen ist. Nach Anklicken der Anzeige wird Ihnen aber eine
Demonstration vorgeführt, die dem eigentlichen
"PreloaderDisplay" recht nahe kommt.
Funktionsweise im Detail
Der Quelltext des "PreloaderDisplay" ist nicht sonderlich
komplex und deshalb schnell erklärt: wie bereits erwähnt, implementiert
die Klasse das "IPreloaderDisplay"-Interface - folglich müssen auch
alle get/set-Methoden implementiert werden:
public function set backgroundAlpha (newAlpha:Number):void {BackgroundAlpha = newAlpha;}; public function get backgroundAlpha ():Number {return BackgroundAlpha;}; public function set backgroundColor (newColor:uint):void {BackgroundColor = newColor;}; public function get backgroundColor ():uint {return BackgroundColor;}; public function set backgroundImage (newImage:Object):void {BackgroundImage = newImage;}; public function get backgroundImage ():Object {return BackgroundImage;}; public function set backgroundSize (newSize:String):void {BackgroundSize = newSize;}; public function get backgroundSize ():String {return BackgroundSize;}; public function set preloader (actualDisplay:Sprite):void { Display = actualDisplay; Display.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress); Display.addEventListener(Event.COMPLETE, onDownloadComplete); Display.addEventListener(FlexEvent.INIT_PROGRESS, onInitializationProgress); Display.addEventListener(FlexEvent.INIT_COMPLETE, onInitializationComplete); }; public function set stageHeight (newHeight:Number):void {StageHeight = newHeight;}; public function get stageHeight ():Number {return StageHeight;};
public function set stageWidth (newWidth:Number):void {StageWidth = newWidth;}; public function get stageWidth ():Number {return StageWidth;};
Die meisten Eigenschaften werden im PreloaderDisplay
gespeichert und bei der Anzeige berücksichtigt, lediglich
"backgroundImage" und "backgroundSize" werden derzeit noch ignoriert.
Sobald die "preloader"-Eigenschaft bekannt ist, werden auch die
benötigten EventListener registriert - auf diese Weise wird das
PreloaderDisplay über den Ladefortschritt informiert.
Da der Konstruktor empfehlungsgemäß leer ist, übernimmt die
Methode "initialize()" die eigentliche Initialisierung:
public function initialize ():void { ... lastProgress = 0; lastTime = getTimer(); lastSpeed = 0; DisplayStartTime = 0; // not really necessary DisplayTimer = new Timer(100); DisplayTimer.addEventListener(TimerEvent.TIMER, onTimerTick); DisplayTimer.start(); };
Zunächst werden die Anzeigefläche gelöscht und alle benötigten
Anzeigekomponenten angelegt (aber noch nicht wirklich angezeigt, die
Details sind hier nicht dargestellt),
danach folgt die Initialisierung aller Variablen für die
Prognoseberechnung. Die (etwas unglücklich benannte) Methode
"getTimer()" stammt aus dem Paket "flash.utils" und liefert die seit
dem Aufruf der Anwendung verstrichene Zeit (in Millisekunden). Da nicht
vorhersehbar ist, wann die oben erwähnten Ereignisse des Preloader
eintreffen, wird die Anzeige durch einen Timer periodisch aktualisiert.
Während des Ladevorganges wird der Fortschritt (hier in
Prozent gemessen) regelmäßig aktualisiert, anschließend folgt eine
Initialisierungsphase bis die Anwendung tatsächlich startbereit ist (im
PreloaderDisplay gekennzeichnet durch "ReadyToRun == true"):
private function onDownloadProgress (theEvent:ProgressEvent):void { Progress = Math.round(theEvent.bytesLoaded/theEvent.bytesTotal*100); }; private function onDownloadComplete (theEvent:Event):void { Progress = 100; }; private function onInitializationProgress (theEvent:FlexEvent):void { /* nop */ }; private function onInitializationComplete (theEvent:FlexEvent):void { ReadyToRun = true; };
Die eigentliche Arbeit wird in der Methode "onTimerTick"
erledigt, die etwa alle 100ms einmal aufgerufen wird. Zunächst wird für
die Prognose ein (konservativer) Mittelwert der Ladegeschwindigkeit
errechnet:
if (Progress < 100) { // calculate current loading speed var ProgressDelta:Number = Progress-lastProgress; var TimeDelta:Number = getTimer()-lastTime; if (TimeDelta > 0) { // just to be on the safe side lastSpeed = lastSpeed*0.9 + ProgressDelta/TimeDelta*0.1; }; lastProgress = Progress; lastTime = lastTime+TimeDelta; };
Anschließend wird entschieden, ob überhaupt eine Anzeige
erfolgen soll:
if (DisplayStartTime == 0) { // indicates a hidden display if ( (lastTime < minHideTime) || // it's too early to show anything ((lastTime < maxHideTime) && ((100-Progress)/lastSpeed < 500)) ) {// it's not useful to show something (works even when lastSpeed == 0) if (ReadyToRun) { DisplayTimer.stop(); dispatchEvent(new Event(Event.COMPLETE)); // VERY important! }; return; // leave without displaying anything }; DisplayStartTime = lastTime; // we start showing something now };
Die Variable "DisplayStartTime" merkt sich den Zeitpunkt der
ersten Anzeige und sorgt so später dafür, daß - wenn eine Anzeige
erfolgt ist - diese mindestens 2 Sekunden lang auf dem Schirm
verbleibt.
Die konkrete Anzeige des Ladefortschritts ist hier nicht
dargestellt - interessant ist am Ende wieder die Erkennung der
Startbereitschaft der eigentlichen Anwendung:
if ((getTimer() >= DisplayStartTime+minDisplayTime) && ReadyToRun) { DisplayTimer.stop(); dispatchEvent(new Event(Event.COMPLETE)); // VERY important! };
Wichtig
ist in diesem Zusammenhang der Versand des "Event.COMPLETE"-Ereignisses
- erst hierdurch wird der Lade- (und Initialisierungs-)Vorgang beendet
unddie Anwendung gestartet. Die Arbeit des "PreloaderDisplay" ist damit
beendet...
Verwendung des PreloaderDisplay in
eigenen Anwendungen
Die hier vorgestellte Preloader-Anzeige läßt sich problemlos
in eigene
Flex-Anwendungen integrieren. Wenn die Optik des "PreloaderDisplay"
nicht
verändert werden soll, ist es ausreichend
- die Quelltextdatei "PreloaderDisplay.as" in das
"src"-Verzeichnis des eigenen "Flex Builder"-Projektes [7]
zu kopieren und
- die MXML-Anweisung für die Flex-Anwendung um die Einträge
usePreloader="true" preloader="PreloaderDisplay"
zu erweitern.
Ansonsten sind vor allem in den Methoden "initialize()" und "handleTimerTick()" zusätzliche
Anpassungen erforderlich. Der Quelltext ist kommentiert, so daß die
erforderlichen Modifikationen leicht von der Hand gehen sollten.
Wichtig ist nach Möglichkeit aber stets der Verzicht auf
Klassen des
Flex-Framework, auch wenn dadurch die Programmierung etwas
komplizierter wird. Denn je mehr Klassen geladen werden müssen, bevor
der Preloader starten kann, desto länger muß der Benutzer auf die
Ladeanzeige warten und desto fraglicher wird der Sinn des zusätzlichen
Aufwandes.
Um das Austesten zu vereinfachen, können in den eben genannten
Methoden drei als "uncomment for testing" markierte Zeilen
auskommentiert werden - dadurch wird zum einen die Mindestzeit für die
Anzeige des Preloader auf fünf Sekunden erhöht und zum anderen der
Fortschritt an der verstrichenen Zeit (und nicht dem tatsächlichen
Ladefortschritt) festgemacht.
Verfügbare Dateien
Folgende Dateien können von hier aus auf den eigenen Rechner
heruntergeladen werden:
Bekannte Probleme
Dem Autor sind bislang keine Probleme bekannt.
Literaturhinweise
[1]
|
Adobe
Flex 3
(siehe http://www.adobe.com/de/products/flex/)
Adobe Flex ist ein (kostenloses) "Framework" für die
Erstellung von Anwendungen auf der Basis von Adobe Flash. Im Gegensatz
zu Flash selbst (welches eher für Grafiker und Web-Designer gedacht
ist), richtet sich Flex explizit an Entwickler und Programmierer.
|
[2]
|
Adobe
Flash Player
(siehe http://get.adobe.com/de/flashplayer/)
Adobe Flash ist eine Plattform für Multimedia-Inhalte
und Anwendungen, die innerhalb eines HTML-Browser angezeigt und
ausgeführt werden. Die Laufzeitumgebung (der "Player") kann kostenlos
installiert und benutzt werden, die Umgebungen für die Entwicklung von
Flash-Inhalten sind z.T. kostenpflichtig.
|
[3]
|
Open
Source Initiative OSI - The MIT License
(siehe http://www.opensource.org/licenses/mit-license.php)
Die
(manchmal auch "X11-Lizenz" genannte) "MIT-Lizenz" ist eine äußerst
einfach gehaltene Lizenz, die die freie Verwendung von Software
sicherstellt und den Autor gleichzeitig von jeglicher Haftung
freistellt.
|
[4]
|
Ted Patrick
Flex 2 Preloaders - SWF, PNG, GIF
Examples
(siehe http://www.onflex.org/ted/2006/07/flex-2-preloaders-swf-png-gif-examples.php)
Dieser Artikel ist die Grundlage vieler anderer
Preloader-Implementierungen, benötigt jedoch eine zusätzliche
Loader-Klasse für das Einbetten von Grafiken und stellt keine Texte dar.
|
[5]
|
Andrew
Implementing A Flex 2 Custom Preloader
(siehe https://defiantmouse.com/yetanotherforum.net/Default.aspx?g=posts&t=82)
Dieser Artikel erweitert den in [4] beschriebenen
Preloader um die
Anzeige von Texten und kommt zugleich ohne eine zusätzliche
Loader-Klasse aus. Stattdessen implementiert er das für eine
Fortschrittsanzeige vorgesehene Interface und macht daraus so etwas wie
eine "abstrakte" Klasse (ohne eigene Anzeige), die in anderen Projekten
eingesetzt werden kann.
|
| [6] |
Sasha Dzeletovic
Custom Flex 3 Lightweight Preloader
with source code
(siehe http://www.pathf.com/blogs/2008/08/custom-flex-3-lightweight-preloader-with-source-code/)
Dieser Artikel erläutert ein paar Randbedingungen für
den Entwurf eines
(sinnvollen) Preloader und liefert ein hübsches Beispiel für die in [5]
beschriebene "abstrakte" Preloader-Klasse.
|
[7]
|
Adobe
Flex Builder
(siehe http://www.adobe.com/de/products/flex/)
Der "Flex Builder" ist eine auf Eclipse aufsetzende
Entwicklungsumgebung für Flex- (und Flash-)Anwendungen von Adobe.
Obwohl nicht kostenlos, ist der "Flex Builder" (vor allem für
Adobe-Verhältnisse) erstaunlich preisgünstig.
|
|
| http://www.Rozek.de/Flex/PreloaderDisplay/index_de.html |
Stand: 04.11.2009 |
|