Ein erster Ausflug in die App-Entwicklung für bada

Seite 2: Praxis

Inhaltsverzeichnis

Doch damit genug der Theorie – die erste Anwendung will erstellt sein. Wie in Abbildung 2 beschrieben, führt der erste Schritt auf bada Developers, wo man sich kostenlos anmeldet. Danach kann der interessierte Leser das bada SDK herunterladen. Nach dem ersten Start des SDK verlangt es, da es auf Eclipse beruht, einen Workspace-Ordner, der mehr oder minder beliebig festgelegt werden darf.

Im nächsten Schritt sei ein Skelettprojekt erstellt, anhand dessen sich der Leser durch die Grundlagen der Plattform durchhangelt. Dazu muss er auf File | New | bada C++/Flash Application Project klicken. Als Projektname wählt er NMGBada1e1, als Projekttyp "bada Form Based Application". Da die restlichen Einstellungen noch nicht interessieren, schließt der Entwickler den Vorgang mit Finish ab. Die IDE beginnt sofort mit der Erstellung des Projektskeletts.

Der in der Datei NMGBada1e1Entry.cpp befindliche Einsprungpunkt ist uninteressant, da er nur die vom Betriebssystem an die Anwendung übergebenen Parameter übersetzt. Da bada aber kein Kommandozeilenbetriebssystem ist, kommt aus dieser Ecke wenig. Das Leben der Anwendung beginnt erst in der Datei NMGBada1e1.cpp. Diese von Osp::App::Application abgeleitete Klasse stellt unter anderem die Funktion CreateInstance bereit, die eine Instanz der Klasse erstellt.

Auch dabei handelt es sich um ein Standardkonstrukt. Die Methode OnAppInitializing ist hingegen weitaus wichtiger, weshalb sie samt den von Samsung vorgegebenen Kommentaren zitiert sei:

bool
NMGBada1e1::OnAppInitializing(AppRegistry& appRegistry)
{
// TODO:
// Initialize UI resources and application specific data.
// The application's permanent data and context can be
// obtained from the appRegistry.
//
// If this method is successful, return true; otherwise,
// return false. If this method returns false, the
// application will be terminated.

// Uncomment the following statement to listen to the screen
// on/off events.
//PowerManager::SetScreenEventListener(*this);

// Create a form
Form1 *pForm1 = new Form1();
pForm1->Initialize();

// Add the form to the frame
Frame *pFrame = GetAppFrame()->GetFrame();
pFrame->AddControl(*pForm1);

// Set the current form
pFrame->SetCurrentForm(*pForm1);

// Draw and Show the form
pForm1->Draw();
pForm1->Show();

return true;
}

Diese Routine nutzt das Framework zum Anwerfen der Anwendung. Ihre Aufgabe ist das Aufrufen des Default-Formulars. Zusätzlich kann man hier die diversen Eventhandler beim System registrieren. Sind beispielsweise Informationen über das Ein- und Ausschalten des Bildschirms gewünscht, kommentiert man die Zeile mit PowerManager::SetScreenEventListener aus.

Der Beispielcode realisiert im nächsten Schritt eine Instanz des Formulars und meldet es beim Frame an. Formulare sind bei bada immer Kind-Elemente des Frame. Jede Anwendung besitzt genau einen Frame. Es kann aber immer nur ein Formular am Bildschirm aktiv sein. Der Frame ist übrigens vom System erstellt und "einfach da" – symbianeske Tricksereien nach dem Schema AppUi gibt es nicht.

SetCurrentForm legt fest, dass das neu erstellte gleichzeitig das aktive Formular sein soll. Es wird mit Draw() auf den Bildschirm gezeichnet und mit Show() aktiviert. Danach wird True zurückgegeben, um den Erfolg der Initialisierung anzuzeigen.

Damit ist der Leser schon in der Form1.cpp-Datei angekommen. Wie bei den meisten in bada verwendeten Objekten ist auch hier der Konstruktor leer:

Form1::Form1(void)
{
}

Form1::~Form1(void)
{
}

Die Header-Datei verrät einige interessante Informationen zu bada im Allgemeinen und zu Formularen im Spezifischen:

class Form1 :
public Osp::Ui::Controls::Form,
public Osp::Ui::IActionEventListener
{

// Construction
public:
Form1(void);
virtual ~Form1(void);
bool Initialize(void);

// Implementation
protected:
static const int ID_BUTTON_OK = 101;
Osp::Ui::Controls::Button *__pButtonOk;

public:
virtual result OnInitializing(void);
virtual result OnTerminating(void);
virtual void OnActionPerformed(const Osp::Ui::Control&
source, int actionId);
};

Das Formular ist sowohl von Form als auch von IActionEventListener abgeleitet. Die Ableitung von Form sorgt dafür, dass ein Formular am Bildschirm erscheint.

Beachtenswert sind außerdem das Feld und die Konstante. Das von der IDE erstellte Formular enthält nämlich einen Button, der durch eine Zahl identifiziert wird. Im Rahmen der Konstruktion nutzt man diese ID, um das Feld mit einem Zeiger auf das Steuerelement zu versehen. Die Definition des Buttons findet sich neben einigen anderen Parametern in einer XML-Datei. Dazu gleich mehr. Vorher noch ein Blick auf die Funktion Initialize(), die das Laden eben dieser Datei erledigt:

bool
Form1::Initialize()
{
// Construct an XML form
Construct(L"IDF_FORM1");

return true;
}

Die eigentliche Initialisierung findet in OnInitializing statt:

result
Form1::OnInitializing(void)
{
result r = E_SUCCESS;

// TODO: Add your initialization code here

// Get a button via resource ID
__pButtonOk = static_cast<Button *>(GetControl(L"IDC_BUTTON_OK"));
if (__pButtonOk != null)
{
__pButtonOk->SetActionId(ID_BUTTON_OK);
__pButtonOk->AddActionEventListener(*this);
}

return r;
}

In bada trifft man häufig auf Methoden, die result als Rückgabewert auswerfen. Dabei handelt es sich um einen speziellen Integer, der den Aufrufer der Methode über ihren Erfolg oder Misserfolg informiert. Im Beispiel wird E_SUCCESS zurückgegeben, um die erfolgeiche Ausführung anzuzeigen. Danach ist der Button zu "beleben". Im ersten Schritt holt man ihn – analog zu Android – mit GetControl aus dem Steuerelemente-Array. Die Typumwandlung auf Button ist nötig, da GetControl generische Control-Zeiger zurückwirft. Der dabei übergebene String ist ebenfalls in der XML-Datei zu finden und dient der Identifikation. Nachdem man den Pointer hat, weist man ihm einen Event Listener und eine Event ID zu. Das Handling der vom Knopf erstellten Events erfolgt in OnActionPerformed:

void
Form1::OnActionPerformed(const Osp::Ui::Control& source, int actionId)
{
switch(actionId)
{
case ID_BUTTON_OK:
{
AppLog("OK Button is clicked! \n");
}
break;
default:
break;
}
}

Die im Listener-Interface vorgeschriebene Methode erhält zwei Parameter: einen Pointer und die ID des auslösenden Steuerelements. Samsung empfiehlt, zum Vergleich auf die IDs zurückzugreifen. Beim Anklicken des Ok-Buttons gibt das Codebeispiel die Meldung "OK Button is clicked!" in der Debugger-Konsole aus. Damit wäre der relevante Code des Beispiels erklärt. Doch das Projekt enthält noch weitere Dateien.

Das Projektverzeichnis enthält ein Unterverzeichnis namens icons. Es beherbergt eine Gruppe Bilddateien, die in verschiedenen Teilen des Betriebssystems aufscheinen. Jedes Icon sollte in mehreren Typen vorliegen. Je nach Bildschirmauflösung wählt das Telefon ein anderes Bild aus. Die Tabellen zeigen die Größen und die Typen an.

Typ Auflösung Systemversion
1 400 x 800 OS 1.x
2 240 x 400 OS 1.x
3 320 x 480 OS 2.x
4 480 x 800 OS 2.x
5 240 x 400 OS 2.x
Name Zwingend Format Typ 1 Typ 2 Typ 3 Typ 4 Typ 5
Main Menu (auf allen vier Seiten des Icons muss ein Pixel breit transparenter Platz bleiben) Ja 32 Bit PNG, mit Alphakanal 100 x 96 50 x 47 50 x 50 80 x 80 42 x 42
Setting Nein 32 Bit PNG, mit Alphakanal 52 x 52 26 x 26 26 x 26 52 x 52 26 x 26
Ticker
(ein 1 Pixel breiter, transparenter Streifen ist auf der Oberseite des Icons erforderlich)
Nein 32 Bit PNG, mit Alphakanal 32 x 32 18 x 18 18 x 18 32 x 32 18 x 18
QuickPanel Nein 32 Bit PNG, mit Alphakanal 68 x 74 34 x 37 26 x 26 52 x 52 26 x 26
LaunchImage Ja PNG oder JPG 480 x 800 240 x 240 320 x 480 480 x 800 240 x 400

Im Programmstarter wird dabei das MainMenu-Icon angezeigt. Klickt man eine Anwendung an, zeigt das Betriebssystem während des Startvorgangs das LaunchImage bildschirmfüllend an.

Übrigens: Die Bilddateien sind in der erst später wichtigen application.xml definiert, deren zugegebenermaßen seltsame Syntax allerdings richtig scheint:

<Icons>
<MainMenu>NMGBada1e1_type4.png<Type1/>
<Type2>NMGBada1e1_type2.png</Type2>
<Type3>NMGBada1e1_type3.png</Type3>
<Type4>NMGBada1e1_type4.png</Type4>
</MainMenu>
<Setting/>
<Ticker/>
<QuickPanel/>
<LaunchImage>Splash_type4.png<Type1/>
<Type2>Splash_type2.png</Type2>
<Type3>Splash_type3.png</Type3>
<Type4>Splash_type4.png</Type4>
</LaunchImage>
</Icons>

Settings -> Notifications und - Tada! (Abb. 3)

Will man ein Setting-, Ticker- oder QuickPanel-Icon hinzufügen, passt man application.xml nach dem obigen Schema an. Das mit Settings bezeichnete Icon kommt nur im Notifications-Menü der Abbildung 3 zur Anwendung, in dem der Anwender festlegen darf, welche Anwendungen Notifications abfeuern dürfen. Hat die eigene Anwendung keine Notifications, braucht man dieses Icon nicht.

Das Ticker-Icon erscheint in der Statuszeile am oberen Rand des Bildschirms, wenn die eigene Anwendung eine Benachrichtigung loslassen will. In der durch Wischen von oben nach unten aufrufbaren "Notification List" kommt das QuickPanel genannte Icon zum Einsatz. Auch die beiden braucht nur, wer Notifications implementiert.

Gewarnt sei davor, dass die vom Hersteller mitgelieferten Bilder auszutauschen sind, bevor man das Programm in Samsungs Store hochlädt. Unterlässt man das, zeigt der QA-Inspektor gnadenlos mit dem Daumen nach unten.

Damit bleibt die Frage, wo das Formular deklariert ist. Die Antwort darauf ist die Datei IDF_FORM1.xml. Klickt man sie an, erscheint die IDE im in Abbildung 4 gezeigten Editiermodus.

Im Editierbereich findet sich eine Übersichtsdarstellung des Formulars. Die Comboboxen auf der Oberseite des Editors erlauben das Zoomen und Adjustieren der Darstellung und des Gitters, an das man die Steuerelemente ausrichtet. Die als Toolbox bezeichnete Leiste dient als Steuerelementliste. Diese Elemente lassen sich aus ihr via Drag &  Drop auf das Formular ziehen, um sie zum Formular hinzuzufügen.

Das Formular ist editierbereit (Abb. 4).

Rechts zeigt die bada IDE statt der dort ausgewählten Liste von Beispielprogrammen die Outline des aktiven Formulars an. Links unten ist die .xml-Datei dargestellt. Sie kann mehr als ein Formular enthalten. Die Einstellungen des gerade aktiven Steuerelements erscheinen in der Ansicht "Properties" am unteren Rand des Bildschirms. Ist sie nicht aktiv, kann man sie – wie in der Java-Version von Eclipse – durch einen Klick auf ihren Header aktivieren. An sich ist der Modus selbsterklärend. Allerdings gibt es ein lästiges Gotcha.

Die Falle Nummer 1 ist das Erstellen eines neuen Formulars, Pop-ups oder Strings. Dazu darf man
keinesfalls auf das File-Menü zugreifen. Stattdessen klickt man den Header des relevanten Typs im "Ressource"-Fenster an und wählt New. Auch die Funktion Generate Class ist mit extremer Vorsicht zu genießen. Anders als ihre namensgleiche Schwester in Carbide.c++ nimmt sie auf den Code keine Rücksicht. Wer einmal den gesamten Code seines Formulars durch das Neugenerieren der Formularklasse verloren hat, ist in Zukunft vorsichtiger.

Das Mitausliefern von Binär-Ressourcen ist ein Problem, seit es Programme gibt. Während Qt das mit dem Ressourcensystem löst, greift man bei Samsung zu einer einfachen und zugleich genialen Lösung. Der gesamte Inhalt der Verzeichnisse /Home/, /Icons/ und /Res/ wandert beim Ausliefern der Anwendung eins zu eins auf das Endgerät des Users.

Wichtig ist, dass auf die in /Icons/ und /Res/ befindlichen Dateien bestenfalls in lesender Weise zugegriffen werden darf. Der Ordner /Home/ hingegen ist das "Installationsverzeichnis", in dem man nach Belieben lesen und schreiben darf. Will man seiner Anwendung Nutzdaten mitgeben, importiert man diese am besten in den Ordner /Home/ des Projekts.

Zu beachten ist auch, dass der Unterordner /Share/ des /Home/-Verzeichnisses mit allen anderen Anwendungen geteilt wird. Es ist daher empfehlenswert, darauf zu achten, dort nichts Sensibles abzulegen.