Englisch version   English version

Motivation

Beim App Inventor gibt es nur eine Möglichkeit der Kommunikation zwischen Screens (Android Activity). Dies ist der StartValue beim Start eines neuen Screen und der Rückgabewert beim Schließen eines Screens. Geöffnete Screens können nicht direkt  untereinander Daten austauschen. Die Extension ermöglicht die direkte Kommunikation zwischen Screens.

Falls etwas fehlt, schreib mir: E-Mail an Ulli.


Versionshistorie

Version Anpassungen
1.0 (2021-02-07) Initiale Version

Download

Verwendung

So funktioniert es

Referenz

Eigenschaften

Funktionen

Ereignisse

Beispiel

CommenEventsTest

Werkzeuge

Download

Das ZIP-Archiv UrsAI2CommonEvents zum Download. Das Archiv enthält den Quellcode, das kompilierte Binary zum Upload in den App Inventor und eine Beispiel-Anwendung.

Verwendung

Hinweis: Da diese Extension mit statischen Variablen arbeitet, ist es nicht sinnvoll, mehr als eine Instanz dieser Extension pro Screen anzulegen.

Die Funktionen RaiseEvent# bzw. PostEvent# lösen das Ereignis OnEvent# auf allen geöffneten Screens aus, die eine Instanz der Extension eingebunden haben. Am einfachsten lässt sich das am im Download enthaltenen Beispiel erklären. Die Beispiel-App (s. Download) implementiert eine "unsichtbaren" Screen1, der Service-Funktionen für Screen2 bereit stellt.

"Unsichtbarkeit" wird erreicht, indem im Screen1 beim Ereignis Screen.OnInitialize direkt Screen2 geöffnet wird. Kehrt die Anwendung von Screen2 zurück, wird die App sofort geschlossen. Screen1 wird also übersprungen:

 Screen1 bietet einen Additions-Service an:

Wenn das Ereignis OnEvent1 auftritt wird, werden die Argumente Id und Data addiert und per Event1 verschickt. RaiseEvent# löst das Ereignis nicht für die Instanz der Extension selbst aus, die es verschickt. Es entstehen somit keine Zirkelschlüsse.

Screen2 inkrementiert / dekrementiert den numerischen Inhalt eines Labels mit Hilfe dieses Services:

RaiseEvent# löst das Ereignis OnEvent# unmittelbar aus. PostEvent# stellt den Auftrag zum Auslösen des Ereignisses in die Message-Queue. Das Ereignis wird erst dann ausgelöst, wenn die aktuelle Kommandosequenz abgearbeitet wurde. Auch dies kann man am Beispiel ausprobieren.

Screen1 stellt einen zweiten Service bereit, der einfach nur den Text "From Screen 1" zurück liefert:

Screen2 ruft den Service per RaiseEvent ab:

 

Nach dem Aufruf von RaiseEvent2 wird unmittelbar das Ereignis OnEvent2 im Screen1 ausgelöst. Dort wird RaiseEvent2 aufgerufen, was direkt OnEvent2 im Screen2 auslöst. In OnEvent2 wird die globale Variable Value gesetzt bevor ihr Inhalt an das Label lblAfter übertragen wird (set lblAfter.text to). lblAfter wird "From Screen 1" anzeigen.

Im zweiten Fall wird der Service per PostEvent abgerufen:

 

Das Ereignis OnEvent2 wird im Screen1 erst dann ausgelöst, wenn die Codesequenz für das Ereignis when comPostTest.Click komplett abgearbeitet ist. lblAfter erhält also noch den alten Wert von Value. Dass der Wert dann doch geändert wurde, kann man sehen, wenn man sich den Wert von Value nachträglich anzeigen lässt. Das Click-Ereignis von cmdGetValue wird ebenfalls über die Message-Queue abgewickelt und erfolgt nach dem Ereignis OnEvent2.

So funktioniert es

Android / App Inventor legt die Screens / Activities auf einem Stapel ab. Der oberste Screen ist der aktive Screen. Er wird angezeigt und erhält Nachrichten von der Benutzeroberfläche.

Mit Control.Open another Screen wird eine neue Instanz einer Screen-Klasse auf den Stapel gelegt und wird zum aktiven Screen. Control.Close Screen entfernt den Screen wieder und zerstört ihn. Der darunter liegende Screen wird nun zum aktiven Screen.

Vorher geöffnete, d.h. weiter unten im Stapel liegende, Screens sind also nicht weg. Sie werden nur nicht angezeigt und erhalten keine Nachrichten von der Benutzeroberfläche. App Inventor bietet von sich aus leider keine Möglichkeit, diese anderen Screens zu referenzieren.

Java bietet die Möglichkeit der statischen (static) Variablen. Dies sind Variablen, die nicht zu einem Objekt, sondern zur Klasse gehören. Sie stehen damit allen Instanzen einer Klasse gleichermaßen zur Verfügung. Die Extension verwaltet eine statische Liste aller angelegten Extension-Instanzen. Wenn eine Instanz erzeugt wird (Konstruktor), trägt sie sich in die Liste eine. Beim Zerstören des Screens (Callback-Funktion onDestroy) wird sie wieder aus der Liste gelöscht.

Die Funktionen RaiseEvent# und PostEvent# iterieren durch diese Liste und lösen somit auf jedem Screen das entsprechende Ereignis aus.

Eine Kleinigkeit gibt es dabei noch zu beachten. Die App Inventor Klasse Form, Basis für die Screen-Klassen, verwaltet eine statische Variable activeForm. In dieser Variablen wird die Instanz des gerade aktiven Screens nachgehalten. Auf activeForm wird an vielen Stellen zugegriffen.

App Inventor geht davon aus, dass nur Screens auf der Spitze des Activity-Stapels Ereignisse auslösen. Im Zuge des Auslösen von Ereignissen wird jedoch activeForm überschrieben mit dem Screen, der zur der Komponenten gehört, die das Ereignis auslöst.

Da durch diese Extension Ereignisse ausgelöst werden, die nicht vom aktiven Screen stammen, würde diese Variable einen falschen Wert erhalten. Deshalb wird vor dem Auslösen der Ereignisse diese Variable gesichert und nach dem Auslösen wieder zurück gespeichert. Leider ist activeForm als geschützt (protected) deklariert, so dass dies per Java-Reflection geschehen muss.

Referenz

Eigenschaften

Version
Ruft die Version der Extension ab.

Funktionen

RaiseEvent#(Id, Data)
Löst unmittelbar das Ereignis OnEvent# bei allen Instanzen der Extension aus, außer bei der, die die Funktion aufruft (keine Zirkelschlüsse). Diese Methode ist fünfmal vorhanden.
Id: Kennung bei Mehrfachverwendung des Ereignisses, kann beliebig auch anderweitig genutzt werden.
Data: Zu übergebende Daten. Müssen mehrere Daten übergeben werden, kann dies in Form einer List oder einer Dictionary geschehen.
PostEvent#(Id, Data)
Stellt den Auftrag zum Auslösen des Ereignis OnEvent# in die Message-Queue. Das Ereignis wird bei allen Instanzen der Extension ausgelöst, außer bei der, die die Funktion aufgerufen hat (keine Zirkelschlüsse). Diese Methode ist fünfmal vorhanden.
Id: Kennung bei Mehrfachverwendung des Ereignisses, kann beliebig auch anderweitig genutzt werden.
Data: Zu übergebende Daten. Müssen mehrere Daten übergeben werden, kann dies in Form einer List oder einer Dictionary geschehen.

Ereignisse

OnEvent#(Source, Id, Data)
Das Ereignis wird durch Aufruf der Funktionen RaiseEvent# bzw. PostEvent# in einem anderen Screen ausgelöst. Dieses Ereignis ist fünfmal vorhanden.
Source: Der Name des Screen-Instanz, die das Ereignis ausgelöst hat.
Id: Kennung bei Mehrfachverwendung des Ereignisses, kann beliebig auch anderweitig genutzt werden.
Data: Zu übergebende Daten. Müssen mehrere Daten übergeben werden, kann dies in Form einer List oder einer Dictionary geschehen
.

Beispiel

CommenEventTest

 

Die meisten Funktionen der Beispiel-App sind oben bereits erklärt.

Werkzeuge

Für die Erstellung eigener Extensions habe ich einige Tipps zusammengestellt: AI2 FAQ: Extensions entwickeln. Auf der Seite findet man noch weitere Hinweise.

Die UML-Diagramme wurden mit PlantUML erzeugt.