Durch einen Artikel in der c't 16/2010 (Seite 96, Autonomie) bin ich auf die Möglichkeit aufmerksam gemacht worden, dass man native Apps für Android und iPhone nicht nur mit den offiziellen Entwicklungsumgebungen erstellen kann, sondern dass es diveres Crossplatform-Tools gibt, mit denen man seine App für mehrere Smartphone-Betriebssysteme entwickeln kann. Aus den vorgestellten Tools hat mir Appcelerator Titanium am Besten gefallen, bei dem man die Apps in JavaScript entwickelt, woraus Titanium dann eine native App compiliert. Also habe ich mich mal etwas damit auseinander gesetzt, eine kleine App geschrieben und einen Artikel dazu verfasst.
Beginnen wir nun also mit unserem ersten selbst geschriebenen Projekt. Da heutzutage ja alle Welt ihre intimsten Gedanken auf Facebook, Twitter und Co. veröffentlicht, wie wäre es mit einer Tagebuch-App?. Diese soll bestehen aus einer Liste mit den Tagebucheinträgen, in der wir neue Einträge hinzufügen, bestehende ändern und löschen können. Desweiteren eine Seite mit Einstellungen, in der wir die maximale Anzahl an Tagebucheinträgen begrenzen können. Dabei behandeln wir folgende Elemente von Titanium:
- TabGroup (Hauptansicht mit Tabreitern)
- TableView (Liste mit mehreren Einträgen)
- Window (Unterfenster zum Eingeben/Ändern von Tagebucheinträgen)
- Buttons und Events (sowohl Standardevents, als auch selbstdefinierte)
- Properties (eigene Daten lesen und schreiben)
- Logging (Infos im Titaniumfenster ausgeben)
Zuerst erzeugen wir in Titanium ein neues Projekt über New Project:

Um den Code modifizieren zu können, müssen wir das Projekt in Eclipse importieren. Das geht am Besten, in dem wir über das Menü File - New - Project aufrufen und dort ein leeres Projekt namens Tagebuch anlegen. Wir müssen dabei darauf achten, ein allgemeines Projekt (General Project) anzulegen:

Auf dem folgenden Bildschirm geben wir dann noch den Projektnamen (Tagebuch) ein und beenden den Wizard mittels Finish. Nun haben wir in unserem Package Explorer ein leeres Projekt mit dem richtigen Namen. Über einen Rechtsklick auf das Projekt und Refresh aktualisieren wir die Anzeige in Eclipse und nach einem Doppelklick auf das Tagebuch-Projekt werden alle Dateien angezeigt, die uns Titanium erzeugt hat.

Zuerst editieren wir die app.js, die wir unter Resources finden. Diese stellt sozusagen das Hauptprogramm unserer Anwendung dar. Dort werden z.B. die Tabs und die dazugehörenden Fenster erzeugt. Wir löschen den bereits existierenden Inhalt und ersetzen ihn durch:
Titanium.API.info('app.js Start.');
var tabMain = Titanium.UI.createTabGroup();
// Das erste Tab enthält die Liste aller Tagebucheinträge
var winListe = Titanium.UI.createWindow({
title:'Tagebuch',
backgroundColor:'#ffffff',
color:'#000000',
url:'js/Liste.js',
exitOnClose: true
});var tabListe = Titanium.UI.createTab({
title:'Tagebuch',
window:winListe
});// Auf das zweite Tab kommen die Einstellungen
var winEinstellungen = Titanium.UI.createWindow({
title:'Einstellungen',
backgroundColor:'#ffffff',
color:'#000000',
url:'js/Einstellungen.js',
exitOnClose: true
});var tabEinstellungen = Titanium.UI.createTab({
title:'Einstellungen',
window:winEinstellungen
});// Und jetzt die Startansicht anzeigen
tabMain.addTab(tabListe);
tabMain.addTab(tabEinstellungen);tabMain.open();
Titanium.API.info('app.js Ende.');
Die einfachste Art in Titanium die Oberfläche einer Anwendungen zusammenzubauen, ist mittels einer TabGroup. In diese kann man beliebig viele Tabs einhängen, die den einzelnen Seiten der Anwendung entsprechen. Jedes Tab benötigt allerdings noch ein Fenster, in dem dann die konkreten Oberflächenelemente dargestellt werden.
Unsere TabGroup besteht aus zwei Tabs, Tagebuch und Einstellungen. Für jeden Tab legen wir ein eigenes Fenster an. Über den url-Parameter bei createWindow kann man die JavaScript-Datei mitgeben, die den Programmcode für das entsprechende Fenster enthält. Daher legen wir auch als nächstes ein neues Unterverzeichnis unter Resources Namens js an und erzeugen in diesem zwei Datei namens Liste.js und Einstellungen.js. Je nach Projektgröße sollte man sich eine übersichtliche Verzeichnisstruktur überlegen. Wir werden einfach alle JavaScript-Dateien in das js-Verzeichnis packen. Erst wenn wir ein wirklich großes Projekt erstellen, lohnt es sich hier weitere Unterverzeichnisse anzulegen, wie z.B. eines für jedes Fenster usw.
Mittels exitOnClose können wir zumindest in der Android-Version festlegen, dass sich die Anwendung beenden soll, wenn im Fenster die Zurück-Taste gedrückt wird. Dies finde ich beim Entwickeln manchmal ganz nützlich, zum Beispiel wenn ich mir mehrmals anschauen möchte, ob der Start der Anwendung und das Laden der Daten einwandfrei funktioniert, denn Android behält die Anwendung ansonsten im Speicher, so dass die Initialisierung bei weiteren Aufrufen nicht mehr durchgeführt wird.
Kümmern wir uns nun um die Liste. Wir legen eine neue Datei namens Liste.js an. In dieser definieren wir zunächst das Tagebuch-Objekt. Dieses ist ein Array aus TagebuchEintraegen. Jeder TagebuchEintrag besteht aus einem Text (Eintrag) und aus einem Wert für unsere Laune (-1: schlecht, 0:mittel, 1:gut gelaunt). Mithilfe der im folgenden definierten Funktionen können wir recht elegant neue Einträge zu unserem Tagebuch hinzufügen.
Titanium.API.info('Liste.js Start.');
//Tagebuch-Liste definieren
var Tagebuch = new Array();// Konstruktor
function TagebuchEintrag(Laune, Eintrag)
{
this.Laune = Laune;
this.Eintrag = Eintrag;
}// Hinzufügen-Methode definieren und setzen
function EintragHinzufuegen(Laune, Eintrag)
{
this[this.length] = new TagebuchEintrag(Laune, Eintrag);
}Tagebuch.EintragHinzufuegen = EintragHinzufuegen;
Wer noch etwas Nachhilfe in JavaScript benötigt, findet im JavaScript-Workshop oder beim Galileo Openbook JavaScript zwei kostenlose Online-Bücher zur Programmiersprache.
Als nächstes lesen wir eventuell bereits vorhandene Daten ein. Dies geschieht über die Funktion Ti.App.Properties.getString bzw. getInt. Mit getInt wird, wie der Name schon andeutet, ein Integerwert gelesen. Mit getString kann man beliebige Datenstrukturen lesen, wenn man diese zuvor über JSON.stringify eingepackt hat. Auspacken können wir diese dann mit JSON.parse. Ob überhaupt etwas in den Properties gespeichert ist, fragen wir zuvor mit hasProperty ab, damit unsere Anwendung nicht auf die Nase fällt, wenn sie das allererste Mal aufgerufen wird. Was und wieviel geladen wurde, protokollieren wir über Titanium.API.Info; eine sehr hilfreiche Funktion, hat Titanium doch keinen Debugger!
// Tagebuch laden
if (Ti.App.Properties.hasProperty('Tagebucheintraege'))
{
Tagebuch = JSON.parse(Ti.App.Properties.getString('Tagebucheintraege'));
Titanium.API.info('Tagebuch geladen (' + Tagebuch.length + ' Einträge).');
}//MaxAnzahl lesen
var MaxAnzahl = 10;
if (Ti.App.Properties.hasProperty('MaxAnzahl'))
{
MaxAnzahl = Ti.App.Properties.getInt('MaxAnzahl');
Titanium.API.info('Max. Anzahl = ' + MaxAnzahl + '.');
}
Nun merken wir uns das aktuelle Fenster in einer lokalen Variablen namens win. Anschließend erzeugen wir einen TableView, über den man Listen sehr einfach darstellen kann.
var win = Titanium.UI.currentWindow;
// Den TableView erzeugen
var tvListe = Titanium.UI.createTableView({
editable:true
});
Das Flag editable sollte auf dem iPhone dafür sorgen, dass man einen Eintrag über einen Wisch von rechts nach links löschen kann. Ausprobieren konnte ich das allerdings noch nicht, da ich zur Zeit auf einem Windows-PC arbeite, auf dem nur die Android-Entwicklungsumgebung läuft. Für die iPhone-Entwicklung benötigt man ja leider zwingend einen Mac.
Für jeden Eintrag in unserem Tagebuch erzeugen wir eine TableViewRow. Wir setzen den Text auf unseren Eintrag und fügend gemäß unserer Laune noch ein Icon hinzu. Die Icons habe ich übrigens in der Wikimedia gefunden und einfach mittels Paint auf eine passende Größe verkleinert. Dann als PNG in das Unterverzeichnis images unter Resources abgespeichert. Mit createTableViewRow erzeugen wir eine neue Row, schreiben mit unserer selbstdefinierten Funktion SetzeTableRow die Inhalte hinein und fügen diese dann mit appendRow zum TableView hinzu. Nachdem wir alle Einträge des Tagebuchs hinzugefügt haben, ergänzen wir noch einen weiteren Eintrag namens "Neuer Eintrag...", über den wir dann später neue Einträge erfassen können.
// Schreibt die Daten in den TableView (insb. setzen der Icons)
function SetzeTableRow(row, n)
{
row.title = Tagebuch[n].Eintrag;
switch (Tagebuch[n].Laune)
{
case -1:
row.leftImage = '../images/sad.png';
break;case 0:
row.leftImage = '../images/plain.png';
break;case 1:
row.leftImage = '../images/smile.png';
break;
}
}// Und die einzelnen Zeilen hinzufügen
for (var n=0; n<Tagebuch.length;n++)
{
var row = Ti.UI.createTableViewRow({color:'#000000'});
SetzeTableRow(row, n);
tvListe.appendRow(row);
}
// Leerer Eintrag für Hinzufügen
var row = Ti.UI.createTableViewRow({title:'Neuer Eintrag...',color:'#404040'});
tvListe.appendRow(row);
Nun benötigen wir einen EventListener, der aufgerufen wird, wenn der Anwender auf eine Zeile des TableViews tippt. Diesen definieren wir über tvListe.addEventListener('click', callback). 'click' ist der Event, auf den wir horchen und callback die Funktion, die aufgerufen werden soll, wenn der Event eintritt. Diese definieren wir direkt inline. Der callback-Funktion wird ein Objekt (e) übergeben, dass die Parameter des Events enthält. Von diesen benötigen wir e.index (den Index der angeklickten Zeile) und e.row (die TableViewRow der angeklickten Zeile). Zur besseren Kontrolle geben wir den Index im Titanium-Fenster aus. Als nächstes öffnen wir dann das Detailfenster, über das wir den ausgewählten Eintrag editieren wollen.
// Bei Klick auf den Eintrag soll ein Detailfenster aufgehen
tvListe.addEventListener('click', function(e)
{
// Index des aktuellen Eintrags und dazugehörige TableViewRow merken
var index = e.index;
var row = e.row;Titanium.API.info('Eintrag (' + index + ') ausgewählt.');
// Öffne Details-Fenster
var wndDetails = Titanium.UI.createWindow({
backgroundColor:'#ffffff'
});
Das Detailfenster bekommt nun die folgenden Oberflächenelemente: eine Textarea zur Eingabe des Eintrags und drei Buttons zur Festlegung der Laune. Da jeder Button wieder einen eigenen EventListener bekommt, wird das folgende Codesegment etwas umfangreicher:
// Das Textfeld für den Eintrag ganz oben, wegen der Tastatur
var txtEintrag = Titanium.UI.createTextArea({
value:(index < Tagebuch.length)? Tagebuch[index].Eintrag : '',
top:20,
width:300,
height:150,
suppressReturn:false
})
wndDetails.add(txtEintrag);// Darunter die Buttons für den Gemütszustand
// Traurig
var btnSad = Titanium.UI.createButton({
backgroundImage:'../images/sad60.png',
borderColor:((index < Tagebuch.length) && (Tagebuch[index].Laune == -1))? '#00ff00' : '#000000',
borderWidth:3,
borderRadius:10,
left:Titanium.Platform.displayCaps.platformWidth/2-110,
top:190,
width:60,
height:60
});
wndDetails.add(btnSad);
// Beim Klick wird dieser Button aktiv (grüner Rahmen)
btnSad.addEventListener('click', function()
{
btnSad.borderColor = '#00ff00';
btnPlain.borderColor = '#000000';
btnSmile.borderColor = '#000000';
});
// Unentschieden
var btnPlain = Titanium.UI.createButton({
backgroundImage:'../images/plain60.png',
borderColor:((index < Tagebuch.length) && (Tagebuch[index].Laune == 0))? '#00ff00' : '#000000',
borderWidth:3,
borderRadius:10,
left:Titanium.Platform.displayCaps.platformWidth/2-30,
top:190,
width:60,
height:60
});
wndDetails.add(btnPlain);
// Beim Klick wird dieser Button aktiv (grüner Rahmen)
btnPlain.addEventListener('click', function()
{
btnSad.borderColor = '#000000';
btnPlain.borderColor = '#00ff00';
btnSmile.borderColor = '#000000';
});
// Glücklich
var btnSmile = Titanium.UI.createButton({
backgroundImage:'../images/smile60.png',
borderColor:((index < Tagebuch.length) && (Tagebuch[index].Laune == 1))? '#00ff00' : '#000000',
borderWidth:3,
borderRadius:10,
left:Titanium.Platform.displayCaps.platformWidth/2+50,
top:190,
width:60,
height:60
});
wndDetails.add(btnSmile);
// Beim Klick wird dieser Button aktiv (grüner Rahmen)
btnSmile.addEventListener('click', function()
{
btnSad.borderColor = '#000000';
btnPlain.borderColor = '#000000';
btnSmile.borderColor = '#00ff00';
});
Um die Buttons unabhängig von der tatsächlichen Displayauflösung halbwegs vernünftig auf der Oberfläche zu verteilen, verwenden wir die Property Titanium.Platform.displayCaps, um die Bildschirmbreite und -höhe auszulesen. Und um den aktiven Laune-Button optisch zu kennzeichnen, verpassen wir ihm einen grünen Rahmen ('#00ff00'). Daher werden in den EventListenern der Buttons einfach nur die Rahmenfarben umgeändert.
Was jetzt noch fehlt, sind die Buttons für Schließen, Speichern und Löschen. Am einfachsten ist der Schließen-Button zu implementieren, da er nur das aktuelle Fenster mittels close() schließen muss.
// Und noch die Buttons Schließen und Speichern
var btnClose = Titanium.UI.createButton({
title:'Schließen',
left:20,
top:270,
width:Titanium.Platform.displayCaps.platformWidth/2-30,
height:30
});
wndDetails.add(btnClose);btnClose.addEventListener('click', function()
{
Titanium.API.info('Eintrag schliessen.');
wndDetails.close();
});
Beim Speichern passiert schon etwas mehr. Zuerst unterscheiden wir, ob ein bestehender Einträg geändert werden oder ob ein neuer Eintrag angelegt werden soll. Im letzteren Fall müssen wir das Tagebuch um einen weiteren Eintrag ergänzen und auch eine neue TableViewRow ans Ende des TableViews hängen. Danach lesen wir den Text aus der Textarea aus und fragen die drei Laune-Buttons ab, wer eine grüne Umrandung hat und setzen die entsprechende Property des Tagebucheintrags. Nun schreiben wir den neuen Eintrag in die TableViewRow, und speichern anschließend das gesamte Tagebuch ab, da man bei einer Smartphone-App ja nie weiß, wann der Anwender diese verlässt. Deshalb ist man gut beraten bei jeder Änderung die Daten direkt wegzuschreiben.
var btnSave = Titanium.UI.createButton({
title:'Speichern',
left:Titanium.Platform.displayCaps.platformWidth/2 + 10,
top:270,
width:Titanium.Platform.displayCaps.platformWidth/2-30,
height:30
});
wndDetails.add(btnSave);btnSave.addEventListener('click', function()
{
if (index < Tagebuch.length)
{
Titanium.API.info('Eintrag speichern.');
}
else
{
Titanium.API.info('Eintrag neu anlegen.');// Neuen Tagebucheintrag anlegen. Dieser benutzt die alte Neuerstellen-Row
Tagebuch[Tagebuch.length] = new TagebuchEintrag(0, '');
Titanium.API.info('Tagebuch hat jetzt ' + Tagebuch.length + ' Einträge.');// Farbe der Neuerstellen-Row auf schwarz ändern.
row.color = '#000000';// Neuen Eintrag für Neuerstellen anfügen
var rowNeu = Ti.UI.createTableViewRow();
rowNeu.title = 'Neuer Eintrag...';
tvListe.appendRow(rowNeu);
}// Geänderte Werte aus der Oberfläche lesen und direkt ins Tagebuch zurückschreiben
Tagebuch[index].Eintrag = txtEintrag.value;
if (btnSad.borderColor=='#00ff00')
{
Tagebuch[index].Laune=-1;
}
else if (btnPlain.borderColor=='#00ff00')
{
Tagebuch[index].Laune=0;
}
else if (btnSmile.borderColor=='#00ff00')
{
Tagebuch[index].Laune=1;
}// TableRow aus Tagebuch neu befüllen
SetzeTableRow(row, index);// Tagebuch speichern
Ti.App.Properties.setString('Tagebucheintraege', JSON.stringify(Tagebuch));// Detailfenster schliessen
wndDetails.close();
});
Zum Schluß kümmern wir uns um das Löschen eines Eintrags. Dieses habe ich der Einfachheithalber ebenfalls über einen Button realisiert. Dieser bekommt eine rote Aufschrift, damit er direkt ins Auge fällt, und wird natürlich nur angezeigt, wenn man einen bestehenden Eintrag ausgewählt hat, und nicht etwa bei der Neuanlage. Zum Löschen des Tagebucheintrags verwende ich die mächtige JavaScript-Funktion splice, die ein (oder bei Bedarf auch mehrere) Element(e) aus dem Array herauslöscht (Schnelle Infos zu JavaScript-Funktionen findet man übrigens auch in SelfHtml). Die entsprechende TableViewRow löschen wir dann mit deleteRow. Anschließend speichern wir das Tagebuch wieder ab und schließen das Detailfenster.
// Ganz zum Schluss noch Löschen (außer beim Neuanlegen)
if (index < Tagebuch.length)
{
var btnDelete = Titanium.UI.createButton({
title:'Löschen',
color:'#ff0000',
top:340,
width:Titanium.Platform.displayCaps.platformWidth-40,
height:30
});
wndDetails.add(btnDelete);btnDelete.addEventListener('click', function()
{
Titanium.API.info('Eintrag löschen (' + index + ').');Tagebuch.splice(index,1);
tvListe.deleteRow(index, {animationStyle:Titanium.UI.iPhone.RowAnimationStyle.UP});// Tagebuch speichern
Ti.App.Properties.setString('Tagebucheintraege', JSON.stringify(Tagebuch));wndDetails.close();
});
}
Was jetzt noch fehlt, wenn man den bisher gelisteten Sourcecode untereinander schreibt, ist das Öffnen des Detailfensters (wir haben ja bisher nur die Oberflächenelemente hinzugefügt), das Schließen der } vom EventListener('click'), und, last but noch least, dass Hinzufügen des TableViews zum aktuellen Fenster.
// Nachdem alles definiert ist, dass Detailfenster öffnen
wndDetails.open();
});// Und hier zum Schluss den TableView auf dem Bildschirm anzeigen
win.add(tvListe);
Damit wäre die Liste fertig und wir können sie sogar schon ausprobieren. Fehlt zum Abschluß noch die Seite mit den Einstellungen. Diese ist relativ simpel gehalten: ein TextField zur Eingabe und ein Button zum Speichern und das war's eigentlich.
Titanium.API.info('Einstellungen.js Start.');
var win = Titanium.UI.currentWindow;
var MaxAnzahl = 10;
//MaxAnzahl lesen
if (Ti.App.Properties.hasProperty('MaxAnzahl'))
{
MaxAnzahl = Ti.App.Properties.getInt('MaxAnzahl');
Titanium.API.info('Max. Anzahl = ' + MaxAnzahl + '.');
}var lbMaxAnzahl = Titanium.UI.createLabel({
text:'Maximale Anzahl der Einträge',
left:30,
top:10,
width:260,
height:'auto'
});
win.add(lbMaxAnzahl);var fldMaxAnzahl = Titanium.UI.createTextField({
hintText:'Maximale Anzahl Einträge',
left:30,
top:35,
width:260,
height:40,
borderStyle:Titanium.UI.INPUT_BORDERSTYLE_ROUNDED,
value:''+MaxAnzahl
});
win.add(fldMaxAnzahl);var btnSpeichern = Titanium.UI.createButton({
title:'Anwenden',
left:30,
top:80,
width:260,
height:30
});
win.add(btnSpeichern);
Aufmerksamen Lesern wird sicher auffallen, dass wir auch hier MaxAnzahl aus den Properties laden. Das hat den Grund, dass jedes Tab einen eigenen Kontext mit eigenen Variablen bekommt, wie ich ja ganz zu Beginn bereits geschrieben hatte. Wie tauschen wir jetzt aber Änderungen an der Variable MaxAnzahl zwischen den Tabs aus? Da der Code der Seite nur ein einziges Mal durchlaufen wird und nicht jedesmal, wenn das Fenster aktiv wird, fällt das simple Nachlesen aus den Properties aus. Wir könnten auf das Event focus lauschen, doch leider bekommt das unter Android nicht das Fenster, sondern nur die TabGroup und die ist ja im Kontext von app.js definiert. Ein Bug oder ein Feature? Schwer zu sagen.
Was leider auch nicht funktioniert ist die Übergabe von Referenzen auf Variablen, wie sie in der Beschreibung zum window-Objekt erklärt wird. Das funktioniert zwar angeblich auf dem iPhone, aber leider mal wieder nicht unter Android. Hierzu existiert auch ein Bugreport, der auf eine Korrektur zum nächsten Release (1.5) hoffen lässt.
Was aber auf jeden Fall funktioniert (zumindest unter Android), ist das Senden eines applikationsweiten Events mittels fireEvent. Dieser kann auf jeder Seite gefangen und bearbeitet werden, also auch in Liste.js. Hier zunächst das Senden, wenn der Speichern-Button gedrückt wird.
// Beim Speichern schicken wir mittels fireEvent eine Nachricht an die Liste
btnSpeichern.addEventListener('click', function()
{
// Nur Speichern, wenn Anzahl geändert wurde
if (MaxAnzahl != fldMaxAnzahl.value)
{
MaxAnzahl = fldMaxAnzahl.value;
Ti.App.Properties.setInt('MaxAnzahl', MaxAnzahl);
Titanium.API.info('MaxAnzahl = ' + MaxAnzahl + '.');// Nun der App (-> Liste.js) Bescheid geben, dass sich MaxAnzahl geändert hat
Titanium.App.fireEvent('MaxAnzahlChanged', {MaxAnzahl:MaxAnzahl});
}
});Titanium.API.info('Einstellungen.js Ende.');
Und am Ende von Liste.js ergänzen wir noch den dazugehörenden EventListener.
//Auf den Event von Einstellungen.js horchen
Titanium.App.addEventListener('MaxAnzahlChanged', function(e)
{
Titanium.API.info('Titanium.App: MaxAnzahl = ' + e.MaxAnzahl + '.');MaxAnzahl = e.MaxAnzahl;
// Das Tagebuch entsprechend kürzen
if (Tagebuch.length > MaxAnzahl)
{
// Merke vorletztes Element für das Löschen der TableViewRows (letztes = Neuanlegen)
var n = Tagebuch.length - 1;// Tagebuch kürzen
Tagebuch.splice(MaxAnzahl, Tagebuch.length - MaxAnzahl);
Titanium.API.info('Tagebuch gekürzt auf ' + Tagebuch.length + ' Einträge.');// Und direkt wieder speichern
Ti.App.Properties.setString('Tagebucheintraege', JSON.stringify(Tagebuch));// Rows des TableViews löschen
for (; n >= MaxAnzahl; n--)
{
Titanium.API.info('Lösche TabViewRow ' + n);
tvListe.deleteRow(n);
}
}
});Titanium.API.info('Liste.js Ende.');
Damit wäre der Sourcecode für unsere erste App komplett. Da vermutlich kaum jemand diesen wirklich abtippt, habe ich die kompletten Quellen inkl. Projektstruktur und Images mal gezippt und auf meinen Webserver gestellt, von dem ihr das Ganze herunterladen könnt. Einfach den Inhalt der ZIP-Datei in eurer Workspace-Verzeichnis entpacken, ein leeres Projekt in Eclipse für dasselbe Verzeichnis anlegen, Refresh aufrufen, und schon könnt ihr auch bequem den Sourcecode in Eclipse ansehen. Zum Ausprobieren dann das Projekt in Titanium mit Import Project hinzufügen und, wie im letzten Kapitel beschrieben, unter Test und Package ausprobieren. Hier mal ein paar Screenshots der Anwendung:

Natürlich funktioniert unser Tagebuch nur rudimentär. Ein paar Details sind nicht schon gelöst oder funktionieren nicht so, wie wir es erwarten würden. Daher habe ich hier ein paar Aufgaben für euch, wie ihr die Applikation noch verbessern könnt:
- Ändert die App so ab, dass die Tagebucheinträge in umgekehrter Reihenfolge erscheinen, die neuesten also immer am Anfang stehen.
- Erweitert die App dann so, dass beim Hinzufügen eines neuen Eintrages geprüft wird, ob die maximale Anzahl Einträge überschritten wird. Falls dies der Fall sein sollte, löscht die ältesten (untersten) Einträge.
Bei weiteren Fragen wendet euch bitte per PN an den Author dieses Blogs.

Stefan S.
würde mich mal interessieren wie das auf dem iPhone aussieht falls du die Möglichkeit hast. Offziell wird man damit ja leider nicht in den Appstore gelassen.