AS3-Tutorium: Flash: Calculator 01 integer: Unterschied zwischen den Versionen
Kowa (Diskussion | Beiträge) |
Kowa (Diskussion | Beiträge) |
||
| Zeile 254: | Zeile 254: | ||
}; | }; | ||
</source> | </source> | ||
===<code>Key</code>=== | ===<code>Key</code>=== | ||
| Zeile 266: | Zeile 267: | ||
'''Anmerkung''': Am [[#Key:_Eine_alternative_Realisierung|Ende des Tutoriums]] wird eine alternative Implementierungs-Möglichkeit für die | '''Anmerkung''': Am [[#Key:_Eine_alternative_Realisierung|Ende des Tutoriums]] wird eine alternative Implementierungs-Möglichkeit für die | ||
Klasse Key vorgestellt, die ohne Flash-Buttons auskommt. | Klasse Key vorgestellt, die ohne Flash-Buttons auskommt. | ||
===<code>EnumKey</code>=== | ===<code>EnumKey</code>=== | ||
| Zeile 296: | Zeile 298: | ||
Sauberer wäre es allerdings, einen echten Enumeration-Typ zu erzeugen, der garantiert, dass nur die | Sauberer wäre es allerdings, einen echten Enumeration-Typ zu erzeugen, der garantiert, dass nur die | ||
gewünschten Werte angenommen werden können. Dies wird im [[AS3-Tutorium: Flash: Calculator 02 float|zweiten Teil des Tutoriums]] realisiert. | gewünschten Werte angenommen werden können. Dies wird im [[AS3-Tutorium: Flash: Calculator 02 float|zweiten Teil des Tutoriums]] realisiert. | ||
===<code>DataCalculator</code>=== | ===<code>DataCalculator</code>=== | ||
| Zeile 328: | Zeile 331: | ||
Die könnte man mit Hilfe von ''User define namspaces''<ref>[http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f32.html Class property attributes ]</ref><ref>[http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9e.htm Packages and namespaces ]</ref> | Die könnte man mit Hilfe von ''User define namspaces''<ref>[http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f32.html Class property attributes ]</ref><ref>[http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9e.htm Packages and namespaces ]</ref> | ||
realisieren, soll hier aber nicht weiter verfolgt werden. | realisieren, soll hier aber nicht weiter verfolgt werden. | ||
===<code>ControllerCalculator</code>=== | ===<code>ControllerCalculator</code>=== | ||
'''Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten''' | '''Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten''' | ||
Die Aufgabe des Controllers (genauer: der Methode <code>keyClick</code>) ist es, | |||
abhängig von der vom Benutzer gedrückten Taste (<code>p_value</code>) die entsprechende | |||
Mehode in der Logik-Komponente aufzurufen und dieser den Tastenwert zu übergeben. | |||
Allerdings ist es (gerade bei der Eingabe über die Tastatur) denkbar, dass der | |||
Benutzer unterschiedliche Tasten drücken kann und dennoch dasselbe Ergebis erzielen will. | |||
Zum Beispiel kann er auf der Tastatur die Enter-Taste oder das das Ist-gleich-Zeichen | |||
drücken, jeweils mit dem Ziel, die Aktion <code>EnumKey.COMPUTE</code> durchzuführen. | |||
Der Controller muss also den von der Benutzer-Aktion übermittelten Tastencode in | |||
durch den zugehöregen Code der Klasse <code>EnumKey</code> ersetzen, | |||
bevor er eine Methode der Logik-Komponente aufruft. Dies kann mit Hilfe von Switch-Anweisungen | |||
oder — sauberer — einer [[Hash-Map (ActionScript)-Hash-Map]] erfolgen. | |||
Damit der Controller auf die Logik-Komponente zugreifen kann, wurde das Add-only-Attribut | |||
<code>logic</code> eingeführt. Die wir folgendermaßen implementiert: | |||
<source lang="actionscript3"> | |||
private var v_logic: LogicCalculator; | |||
public function set logic (p_logic: LogicCalculator): void | |||
{ | |||
v_logic = p_logic; | |||
} | |||
</source> | |||
Das heißt, es gibt eine (private) Zustandsvariable, in der die Logik-Komponente gespeichert wird, | |||
sowie eine Setter-Methode zum Initialisieren dieser Variablen. Die Initialisierung wird von | |||
der Main-Klasse <code>Calculator01Flash</code> beim Programmstart vorgenommen. | |||
Falls nicht nur Eingaben per Maus-Klick (über die View) möglich sein sollen, sondern auch Tastatur-Eingaben, | |||
wird ein zweites Add-only-Attribut benötigt, das von <code>Calculator01Flash</code> beim Programmstart | |||
initialisiert wird: <code>stage</code>. | |||
Hier ist es gar nicht notwendig, das Stage-Objekt in einer Zustandsvariablen zu speichern. | |||
Es reicht aus, dem Stage-Objekt einen Event-Listener zuzuordnen, der dafür sorgt, dass bei jedem Tastenklick | |||
eine interen Methode aufgerufen wird, die diesen verarbeitet: | |||
<source lang="actionscript3"> | |||
public function set stage(p_key_input: Stage): void | |||
{ | |||
p_key_input.addEventListener(KeyboardEvent.KEY_DOWN, o_key_input); | |||
} | |||
</source> | |||
Die Methode <code>o_key_input</code> muss wiederum Tastenklicks in Methodenaufrufe | |||
der Logik-Komponente umsetzen. Dabei ist allerdings eine große Unsauberkeit von | |||
ActionScript zu beachten: Die Methode <code>charCode</code> der Klasse | |||
</code>KeyboardEvent</code> liefert stets das Zeichen, dass auf einer | |||
englischen Tastatur an dieser Stelle zu finden wäre. Wenn man auf einer deutschen Tastatur | |||
beispielsweise die Ist-gleich-Taste (<code>"="</code>)drückt, liefert <code>String.fromCharCode(p_event.charCode)</code> | |||
die geschlossene Klammer (<code>")"</code>) als Ergebnis. | |||
Wenn man also die Methode <code>charCode</code> verwendet, muss man theorietisch für jede denkbare Tastatur | |||
einen eigenen Tastatur-Contoller schreiben. | |||
Als Workaround kann man ein unsichtbares Textfeld (<code>alpha == 0</code>) über die gesamte Bühne legen, | |||
diesem den Eingabefokus geben und alle Tastaturereignisse von diesem Feld abfangen. Dies funktioniert, | |||
da bei der Eingabe in ein Textfeld das von Benutzer im Betriebssystem eingestellte Tastatur-Layout | |||
beachtet wird. Allerdings ist diese Art der Implementierung wesentlich aufwändiger und soll hier nicht | |||
weiter verfolgt werden. | |||
Version vom 6. Dezember 2010, 09:32 Uhr
Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:
| Korrektheit: 4 (großteils überprüft) |
Umfang: 1 (zu gering) |
Quellenangaben: 5 (vollständig vorhanden) |
Quellenarten: 5 (ausgezeichnet) |
Konformität: 5 (ausgezeichnet) |
AS3-Tutorium: Calculator: Flash | Flex
Flash: Übersicht | Teil 1 | Teil 2
Taschenrechner
In diesem Teil des Tutoriums wird beschrieben, wie ein einfacher Taschenrechner implementiert werden kann. Dieser kann nur Interzahlen verarbeiten. Auf Komma- und Exponential-Darstellungen wird ebenso verzichtet, wie auf eine Fehlerbehandlung bei einer Division durch Null.
| <swf width="300" height="420">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/calculator/calculator_01_integer/Calculator01Flash11.swf</swf> |
<swf width="300" height="420">http://glossar.hs-augsburg.de/beispiel/tutorium/flex_4/calculator/calculator_01_integer/Calculator01Flex.swf</swf> Musterlösung Flex (SVN-Repository) |
Datenmodell
Im Folgenden wird ein Klassendiagramm beschrieben, dass die Implementierung
des obigen Taschenrechners gemäß dem LDVCS-Paradigmas ermöglicht.
Den für dieses Model von StarUML erzeugten Code-Rahmen finden Sie
im Ordner staruml_code der Musterlösung.
Beachten Sie allerdings, dass die Attribute im Allgemeinen mit Hilfe von Getter- und Setter-Methoden implementiert werden müssen.
Insbesondere für die Attribute data, controller, logic und keyInput, die von der Main-Klasse Calculator01Flash initialisiert werden,
sollten nur Setter-Methoden definiert werden. Auf diese Attribute wird niemals von außerhalb zugegriffen.
Medium:AS3-Tutorium Calculator 01 Flash Class Diagram.jpg
Vier LDVCS-Komponenten
Aus folgenden vier LDVCS-Komponenten besteht der Taschenrechner (eine Service-Komponente gibt es nicht, da der Taschenrechner weder konfiguriert werden muss noch mit anderen Anwendungen kommuniziert):
ViewCalculator- Die View-Komponente enthält ein Panel bestehnd aus Digitalziffern (ein Vorzeichen-Element und acht Sieben-Segment-Elemente) zum Darstellen des Rechenergebnisses. Außerdem enthält sie 16 Tasten zur Eingabe von Ziffern und Operatoren.
ControllerCalculator- Der Controller bietet eine Methode
keyClick, die von der View aufgerufen wird, sobald eine Taste gedrückt wird. Dieser Methode wird im Parameterp_valueübergeben, welche Taste gedrückt wurde:0, ..., 9, C, =, +, -, ×, ÷. Der Controller überfügt über zwei Attribute, die bei Programmstart initialisiert werden:stageenthält das Stage-Objekt, um auf Tastatur-Ereignisse direkt zugreifen zu können. Damit kann der Taschenrechner auch über die Tastatur gesteuert werden.logicenthält die Logik-Komponente der Anwendung. Die Aufgabe des Controllers ist es, die Methoden dieser Komponenten abhängig von der Benutzer-Eingabe aufzurufen.
LogicCalculator- Aufgabe der Logik-Komponente ist es, abhängig von der Benutzeraktion ein neues Ergebnis zu berechnen und dieses im Data-Objekt zu speichern.
- Es gibt drei mögliche Benutzeraktionen:
- Drücken einer Ziffer:
addDigit - Drücken einer Operatortaste:
addOperator - Drücken der Clear-Taste:
clear
- Drücken einer Ziffer:
DataCalculator- Das Daten-Objekt enthält diejenigen Daten die von der View visualisiert werden können. Für den in der Musterlösung vorgestellten Taschenrechner braucht das Daten-Objekt nur eine einziges Atribut zu enthalten:
value: Die Zahl, die aktuell erfasst wird bzw, das Ergebnis, nachdem eine mathematische Operation ausgeführt wurde.
Es ist aber durchaus sinnvoll, weitere Attribute für die Visualisierung bereitzustellen (siehe oben: Musterlösung Flex):
operator: Der Operator, der als nächstes angewendet werden soll (wenn die aktuelle Zahl vollständig eingegeben wurde).oldValue: Die letzte Zahl, die erfasst bzw. gerechnet wurde. Diese wird mit der aktuellen Zahl (mit Hilfe des zuvor eingegebenen Operators) verknüpft, sobald der nächste Operator ("=", "+", "-", "×", "÷") gedrückt wird.
Separation of Concerns
Jede der im Diagramm angegebenen Klassen hat genau eine Aufgabe (vgl. Programmierprinzipen „Separation of Concernse“ und „Single responsibility principle“):
LED
Visualisierung einer LED (Leuchtdiode), die ein- und ausgeschaltet werden kann.
LEDRed,LEDPlusRed,LEDMinusRed
Flash-Symbole, die rote LEDs visualisieren und LED als Basisklasse haben. (Man könnte auf die Klassen LEDPlusRed und LEDMinusRed auch verzichten, wenn LEDRed rechteckig ist. Dann kann man ein Plussymbol einfach durch zwei LEDs symbolisieren.)
Sign
Visualisierung eines Vorzeichen-Elements, bestehend aus ein bis drei LEDs. In der Musterlösung kann das Element drei Werte annehmen: „positiv“, „negativ“ und „ausgeschaltet“. Es würde aber auch reichen, nur ein Minuszeichen als Vorzeichen zu verwenden. In diesem Fall würden sich die Zustände „positiv“ und „ausgeschaltet“ bei der Visualisierung nicht unterscheiden (siehe oben: Musterlösung Flex).
Digit
Visualisierung eines Sieben-Segment-Elements, bestehend aus sieben LEDs. In der Musterlösung kann ein deratiges Element ide Ziffern 0 bis 9 darstellen sowie ausgeschaltet sein. Weitere Werte (wie z.B. ein E für Error) sind denkbar.
Panel
Visualisierung einer Intergerzahl mit maximal acht Stellen.
Key
Visualisierung eines Taschenrechner-Buttons für die Benutzereingabe. In der Musterlösung wurde allerdings nicht Button als Basisklasse verwendet, sondern MovieClip, da die Skinning-Möglichkeiten von Flash-Buttons eingeschränkt sind. In der Flex-Musterlösung wurden dagegen Flex-Spark-Buttons verwendet. Deren Aussehen kann problemlos gemäß den eigenen Wünschen verändert werden (Skinning).
ViewCalculator- Verknüpfung der anderen Komponenten mit der View:
- Das Panel muss den Wert des Attributs
valueder Daten-Komponente darstellen. - Die Keys müssen, sobald sie geklickt werden, ihren Label-Wert an die Methode
keyClickder Controller-Komponenten weiterleiten.
- Das Panel muss den Wert des Attributs
ControllerCalculator- Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten. Dabei werden zwei Arten von Eingabe unterstützt:
- Eingabe per Maus-Klick (View-Komponente)
- Eingabe per Tastatur (Listener für Keyboard-Events)
LogicCalculator- Aktualisieren der Werte in der Daten-Komponente nach jeder Benutzerkation.
DataCalculator- Verwaltung der Daten, die visualisiert werden sollen:
- Speichern der Daten
- Informieren der interessierten Observers über Änderungen
Calculator01Flash- Starten der Anwendung, insbesondere Erzeugen und Initialisieren aller LDVCS-Komponenten.
EnumKey- Bereitstellung von Konstanten, die im Controller, in der Logikkomponente sowie im Daten-Objekt verwendet werden.
Anmerkung: Das Prinzip Programmierprinzip „Single responsibility principle“ wurde nicht zu 100% umgesetzt. So könnten z.B. zwei Controller-Komponenten definiert werden,
eine für die Verarbeitung von Bildschirmeingaben und eine für die Verarbeitung von Tastatureingaben. Genauso könnte die Klasse ViewCalculator durch zwei Klassen ersetzt werde:
Eine zur Initialisierung der Daten-Ausgabe (Panel) und eine zur Initialisierung der Benutzereingabe (Keys).
Ziel des „Single responsibility principle“ ist immer, dass es für keine Klasse mehrere Gründe geben soll, warum sie geändert werden muss.
Zum Beispiel muss derzeit die Klasse ControllerCalculator geändert werden, wenn sich die Tastatureingabe ändert (was z.B. bei einem Umstieg von einer deutschen auf eine französische Tastatur notwendig wird, da der KeyboardEvent nur die Character Codes einer englischen Tastatur liefert und daher die Controller-Klasse an jede Tastatur angepasst werden muss). Aber auch, wenn die Symbole der Tasten, die per Maus aktiviert werden, geändert werden sollen, muss diese Klasse modifiziert werden, obwohl der eine Änderungswunsch nichts mit dem anderen zu tun hat.
Implementierung
LED
Visualisierung einer LED (Leuchtdiode)
Diese Klasse enhält ein Attribut value, das die beiden Werte
LED.OFF und LED.ON annehmen kann.
Das Attribut wird mit Hilfe eines Getter-/Setter-Paares realisiert:
public function get value(): int {...}
public function set value(p_value: int): void {...}
Die Getter-Methode liefert als Ergebnis den aktuellen Status der LED. Dieser
wird aus einer privaten Zustandsvariablen ausgelesen. Auf diese Methode könnte sogar verzichtet
werden, da es nicht die Aufgabe einer View ist, Auskunft über seinen Zustand zu geben.
Eine View dient lediglich dazu, einen Zustand zu visualisieren. Allerdings ist
die Getter-Methode für das Debugging vorteilhaft, um den aktuellen Wert, den die
LED visualisieren soll, per trace auszugeben.
Sofern die Getter-Methode implementiert wurde, muss
die Setter-Methode den neuen Wert in der zuvor genannten Zustandsvariablen speichern.
Ihre eigentliche Aufgabe ist jedoch, den neuen Wert zu visualisieren,
indem sie das zugeörige Objekt this (dem die Klasse LED als Basisklasse zugeordnet ist) manipuliert.
Dafür gibt es mehrere Möglichkeiten:
- die Eigenschaft
visibledes Symbols auftrueoderfalsesetzen - den Alphawert des Symbols verändern
- zu einer dem Wert ensprechenden Stelle in der Timeline des Symbols springen
Sign und Digit
Visualisierung eines Vorzeichen-Elements und eines Sieben-Segment-Elements
Diese Klassen enhalten ein Attribut value, das diverse Werte annehmen kann. Wie zuvor wird es mit einem
Getter-/Setter-Paar realisiert, wobei auf die Getter-Methode wieder (aus demselben Grund wie zuvor) verzichtet werden könnte:
public function get value(): int {...}
public function set value(p_value: int): void {...}
Die wichtigste Aufgabe der Setter-Methode ist die Visualisierung des übergebenen Wertes mit Hilfe von LEDs. Dafür werden
in einem zugehörigen Symbol (das heißt, in einem Symbol, dem die Klasse Sign bzw. Digit als Basisklasse zugeordnet wurde)
LED-Symbole eingefügt und mit „Instanznamen“ (wie z.B. sp_led_0, sp_led_1, ...) versehen. Die Aufgabe der Setter-Methode ist es nun, die LEDs (zum Beispiel mit Hilfe einer Schleife) zu initialisieren:
private static const c_number_of_leds: uint = ...;
private static const c_led_prefix: String = "sp_led_";
private static const c_led_states: Array = [ [LED.ON, LED.OFF, ...],
[LED.OFF, LED.ON, ...],
...
];
for (var i: int = 0; i < c_number_of_leds; i++)
this[c_led_prefix + i].value = c_led_states[p_value+1][i];
Die Konstante c_led_states enthält ein Array von Arrays.
Für jeden möglich value-Wert ist im äußeren Array ein Array enthalten, welches
für jede LED beschreibt, ob sie ein- oder ausgeschaltet werden muss, wenn dieser Wert visualisiert werden soll.
Ein Sieben-Segment-Element enthält sieben LEDs. Das Vorzeichen-Element enthält ein bis drei LEDs:
- ein Minus-Symbol (hier besteht kein Unterschied zwischen
POSITIVEundOFF) - ein Minus- und ein Plus-Symbol
- ein Minus- und ein Plus-Symbol, wobei letzteres aus zwei LED zusammengesetzt wird
Panel
Visualisierung einer Intergerzahl
Die Aufgabe eines Panel-Objektes ist es wiederun eine Integerzahl zu visualisieren.
Diesmal soll jedoch nicht nur eine Ziffer, sondern eine ganze Zahl (einschließlich Vorzeichen)
dargestellt werden. Dazu werden dem Panel ein Vorzeichenelemnt (sp_sign) sowie
acht Ziffern-Elemente zugeordnet:
sp_digit_7 bis sp_digit0; das niederwertigste Element befindet sich ganz rechts.
Wie zuvor gibt es eine Getter- und eine Setter-Methode für den Zugriff auf das
Attribut value (wobei die Getter-MEthode wiedrun nicht unbedingt nötig ist):
public function get value(): int {...}
public function set value(p_value: int): void {...}
Die Implementierung der Setter-MEthode ist allerdings etwas aufwändiger als zuvor.
Zunächst muss der übergebene Wert gesichert werden (sofern man die Getter-Methode implementiert):
v_value = p_value;
Als nächstes muss sichergestellt werden, dass der übergebene Wert
nicht zu groß oder zu klein ist, um die Zahl mit acht Ziffern darzustellen.
Falls dies nicht der Fall ist, wird der übergebene Wert durch die
größte bzw. kleinste darstellebare Zahl ersetzt (wobei c_max_value == Math.pow(10,8)-1 gilt):
if (p_value < -c_max_value)
p_value = -c_max_value;
else if (p_value > c_max_value)
p_value = c_max_value;
Nun kann der Wert des Vorzeichens bestimmt werden.
Als Seiteneffekt wird p_value durch seinen Absolutbetrag ersetzt,
damit für die nachfolgenden Befehle p_value >= 0 garantiert werden kann:
if (p_value > 0)
sp_sign.value = Sign.POSITIVE;
else if (p_value < 0)
{
sp_sign.value = Sign.NEGATIVE;
p_value = -p_value;
}
else
sp_sign.value = Sign.OFF; // 0 hat kein Vorzeichen!
Als nächstes werden die Ziffern-Elemente (von rechts nach links) auf die korrekten Werte gesetzt,
wobei führende Nullen nicht beachtet werden. Die do-while-Schleife garantiert, dass
die Zahl Null nicht mit null Ziffern, sondern mit einer Null visualisiert wird.
var i: int = 0;
do
{
this[c_digit_prefix + i].value = p_value % c_base;
p_value /= c_base;
i++;
}
while (p_value > 0);
Zu guter Letzt werden die restlichen Ziffern ausgeschaltet:
while (i < c_number_of_digits)
{
this[c_digit_prefix + i].value = Digit.OFF;
i++;
};
Key
Visualisierung eines Taschenrechner-Buttons
Ein normaler Flash-Button kann als Eingabe-Taste für den Taschenrechner verwendet werden.
Die Klasse Key hat dann nur eine Aufgabe. Im Konstruktor Key()
können die Buttoneigenschaften (wie z.B. Fontart und -größe des Button-Labels) festgelegt werden
(siehe Dokumentation von Adobe → Examples).
Button-Layout-Änderungen sind allerdings relativ aufwändig.[1]
Anmerkung: Am Ende des Tutoriums wird eine alternative Implementierungs-Möglichkeit für die Klasse Key vorgestellt, die ohne Flash-Buttons auskommt.
EnumKey
Bereitstellung von Konstanten
Nachdem nun die Klassen zur Visualisierung von Integerahlen und zur Eingabe von Rechenoperationen existieren, ist es an der Zeit, die
eigentlichen LDVCS-Klassen (ViewCalculator, ControllerCalculator, LogicCalculator, DataCalculator) zu implementieren.
Dies Klassen verarbeiten Ziffern und mathematische Operatoren, die vom Benutzer per Tastendruck übergeben werden.
Die Klasse EnumKey simuliert einen Enumeration-Datentyp, der
alle erlaubten Tasten-Werte enthält, indem sie für jeden Tastenwert eine statische Konstante definiert.
Im Prinzip kann die von StarUML erzeugte Implementierung dieser Klasse
ohne besondere Änderungen übernommen werden:
package calculator.enum
{
public static const DIGIT: int = 0;
public static const OPERATOR: int = 1;
public static const CLEAR: String = "C";
public static const COMPUTE: String = "=";
public static const ADD: String = "+";
public static const SUBTRACT: String = "-";
public static const MULTIPLY: String = "×";
public static const DIVIDE: String = "÷";
}
}
Sauberer wäre es allerdings, einen echten Enumeration-Typ zu erzeugen, der garantiert, dass nur die gewünschten Werte angenommen werden können. Dies wird im zweiten Teil des Tutoriums realisiert.
DataCalculator
Verwaltung der Daten, die visualisiert werden sollen
Die Aufgabe der Daten-Komponente ist es, diejeingen Daten zur Verfügung zu stellen, die von der View visualisiert werden sollen. Die Logik-Komponente schreibt und liest diese Werte.
Folgende Implementierung wird von StarUML erzeugt (plus einem Default-Konstruktor).
public class DataCalculator extends EventDispatcher
{
public var value: int = 0;
}
Allerdings ist diese Implementierung unvollständig, da eine Änderung des Wertes der View
mit Hilfe eines Signals (Events) angezeigt werden muss. Das heißt, man muss das
Attribut mit Hilfe einer privaten Variablen (z.B. v_value) und einem Getter-/Setter-Paar
implementieren.
Die Getter-Methode liefert den Wert der privaten Variablen als Ergebnis.
Die Setter-Methode speichert den übergebenen Wert in der lokalen Variablen und
siganlisiert dann die Änderung dieser Variablen mit einem Event:
this.dispatchEvent(new Event(Event.CHANGE));
Ganz sauber wäre es, wenn nur die Logik-Komponente auf die Setter-Methode Zugriff hätte. Die könnte man mit Hilfe von User define namspaces[2][3] realisieren, soll hier aber nicht weiter verfolgt werden.
ControllerCalculator
Aufbereitung der Benutzer-Eingaben für die Logik-Komponenten
Die Aufgabe des Controllers (genauer: der Methode keyClick) ist es,
abhängig von der vom Benutzer gedrückten Taste (p_value) die entsprechende
Mehode in der Logik-Komponente aufzurufen und dieser den Tastenwert zu übergeben.
Allerdings ist es (gerade bei der Eingabe über die Tastatur) denkbar, dass der
Benutzer unterschiedliche Tasten drücken kann und dennoch dasselbe Ergebis erzielen will.
Zum Beispiel kann er auf der Tastatur die Enter-Taste oder das das Ist-gleich-Zeichen
drücken, jeweils mit dem Ziel, die Aktion EnumKey.COMPUTE durchzuführen.
Der Controller muss also den von der Benutzer-Aktion übermittelten Tastencode in
durch den zugehöregen Code der Klasse EnumKey ersetzen,
bevor er eine Methode der Logik-Komponente aufruft. Dies kann mit Hilfe von Switch-Anweisungen
oder — sauberer — einer Hash-Map (ActionScript)-Hash-Map erfolgen.
Damit der Controller auf die Logik-Komponente zugreifen kann, wurde das Add-only-Attribut
logic eingeführt. Die wir folgendermaßen implementiert:
private var v_logic: LogicCalculator;
public function set logic (p_logic: LogicCalculator): void
{
v_logic = p_logic;
}
Das heißt, es gibt eine (private) Zustandsvariable, in der die Logik-Komponente gespeichert wird,
sowie eine Setter-Methode zum Initialisieren dieser Variablen. Die Initialisierung wird von
der Main-Klasse Calculator01Flash beim Programmstart vorgenommen.
Falls nicht nur Eingaben per Maus-Klick (über die View) möglich sein sollen, sondern auch Tastatur-Eingaben,
wird ein zweites Add-only-Attribut benötigt, das von Calculator01Flash beim Programmstart
initialisiert wird: stage.
Hier ist es gar nicht notwendig, das Stage-Objekt in einer Zustandsvariablen zu speichern. Es reicht aus, dem Stage-Objekt einen Event-Listener zuzuordnen, der dafür sorgt, dass bei jedem Tastenklick eine interen Methode aufgerufen wird, die diesen verarbeitet:
public function set stage(p_key_input: Stage): void
{
p_key_input.addEventListener(KeyboardEvent.KEY_DOWN, o_key_input);
}
Die Methode o_key_input muss wiederum Tastenklicks in Methodenaufrufe
der Logik-Komponente umsetzen. Dabei ist allerdings eine große Unsauberkeit von
ActionScript zu beachten: Die Methode charCode der Klasse
KeyboardEvent liefert stets das Zeichen, dass auf einer
englischen Tastatur an dieser Stelle zu finden wäre. Wenn man auf einer deutschen Tastatur
beispielsweise die Ist-gleich-Taste ("=")drückt, liefert String.fromCharCode(p_event.charCode)
die geschlossene Klammer (")") als Ergebnis.
Wenn man also die Methode charCode verwendet, muss man theorietisch für jede denkbare Tastatur
einen eigenen Tastatur-Contoller schreiben.
Als Workaround kann man ein unsichtbares Textfeld (alpha == 0) über die gesamte Bühne legen,
diesem den Eingabefokus geben und alle Tastaturereignisse von diesem Feld abfangen. Dies funktioniert,
da bei der Eingabe in ein Textfeld das von Benutzer im Betriebssystem eingestellte Tastatur-Layout
beachtet wird. Allerdings ist diese Art der Implementierung wesentlich aufwändiger und soll hier nicht
weiter verfolgt werden.
ViewCalculator
Verknüpfung der anderen Komponenten mit der View
LogicCalculator
Aktualisieren der Werte in der Daten-Komponente
Mealy-Automat
Medium:AS3-Tutorium Calculator 01 Mealy Machine.jpg
===Calculator01Flash=== Starten der Anwendung, insbesondere Erzeugen und Initialisieren aller LDVCS-Komponenten
Key: Eine alternative Realisierung
Visualisierung eines Taschenrechner-Buttons
Wie im Anschnitt Key angekündigt wurde, ist es relativ einfach möglich, die Eingabe-Tasten des Taschenrechners
(Klasse Key) auch ohne Zuhilfenahme von Flash-Buttons zu realisieren.
Im Prinzip funktionert dies, wie im Tutorium Butterfly 07a character beschrieben.
Man erzeugt ein Symbol, in dessen Zeitleiste die verschiedenen Zustände des Buttons
in verschiedenen Frame-Blöcken visualisiert werden. Jeder dieser Blöcke beginnt mit einem Label (lb_up, lb_down und evtl. weitere wie z.B. lb_over für Mouse-Over-Effekte) und endet mit einem stop()-Befehl.
In einer eigenen Ebene wird blockübergreifend ein Text-Feld eingefügt, das den Namen sp_label erhält.
Diesem Symbol wird die Klasse Key als Basis-Klasse zugewiesen:
package calculator.view.key
{
import fl.text.TLFTextField; // Nur Flash CS5!
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.MovieClip;
public class Key extends MovieClip
{
/////////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////////
private static const c_lb_up: String = "lb_up";
private static const c_lb_down: String = "lb_down";
/////////////////////////////////////////////////////////////////////////////
// Instance variables
/////////////////////////////////////////////////////////////////////////////
public var sp_label: TLFTextField; // Nur Flash CS5, sonst fl.controls.Label
/////////////////////////////////////////////////////////////////////////////
// Attributes
/////////////////////////////////////////////////////////////////////////////
[Inspectable(name="label", variable="label", type="String", defaultValue="0", enumeration="0,1,2,3,4,5,6,7,8,9,C,=,+,−,×,÷")]
public function get label(): String
{
return sp_label.text;
}
public function set label(p_label: String):void
{
sp_label.text = p_label;
}
/////////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////////
public function Key()
{
super();
this.addEventListener(MouseEvent.MOUSE_UP, o_mouse_up);
this.addEventListener(MouseEvent.MOUSE_DOWN, o_mouse_down);
}
/////////////////////////////////////////////////////////////////////////////
// Observers
/////////////////////////////////////////////////////////////////////////////
private function o_mouse_up(p_event: Event): void
{
this.gotoAndPlay(c_lb_up);
}
private function o_mouse_down(p_event: Event): void
{
this.gotoAndPlay(c_lb_down);
}
/////////////////////////////////////////////////////////////////////////////
// End of class
/////////////////////////////////////////////////////////////////////////////
}
}
Man beachte die Meta-Anweisung Inspectable. Damit wird Flash mitgeteilt,
dass der Label-Wert eines Key-Objektes auf der Bühne über den Flash-Eigenschaften-Inspektor
festgelegt werden kann. Um dies zu erreichen, muss die Klasse calculator.view.key.Key
nicht nur als Basisklasse im Eigenschaften-Fenster des Symbols eingetragen werden, sondern
zusätzlich auch als Klasse unter Komponentendefinition des Symbols (Klick mit rechter Maustaste auf das Icon vor dem Symbol).
Sollte nach erneutem Öffnen des Komponentendefinition-Inspektors der Parameter label
in der Parameterliste nicht erscheinen (dieses Problem besteht bei Flash CS5, aber nicht bei Flash CS5),
muss dieser von Hand eingetragen werden:
Name: label, Variable: label, Wert: 0, Typ: String
Nun kann jedem Key-Symbol, das auf die Bühne gelegt wird, ein Label-Wert über
das Fenster Eigenschaften → Komponentenparameter (bzw. bei Flash CS4: Komponenteninspektor)
zugewiesen werden. Allerdings wird dieser Wert nicht auf der Bühne angezeigt, sondern nur bei Start der zugehörigen SWF-Datei.
