Now playing Titanium (Mobile Technology 2013)

9
www.mobile360.de Mobile Technology 1 | 2013 086 Mobile Web | Titanium von Peter Friese Titanium Mobile von Appcelerator ist ein SDK zur platt- formübergreifenden Entwicklung von Apps für mobile Geräte. Derzeit werden die beiden Plattformen iOS und Android unterstützt, der Support für BlackBerry (auf Ba- sis von BB 10) befindet sich in der Entwicklung [1]. Eine native Unterstützung von Windows Phone wird immer mal wieder diskutiert, ist derzeit aber nicht verfügbar. Titanium verfolgt einen hybriden Ansatz, d. h. Teile der Applikation werden als nativer Code ausgeführt, wäh- rend andere Teile in JavaScript implementiert werden. Dazu stellt Titanium eine Abstraktionsschicht zur Ver- fügung, die es dem Entwickler erlaubt, seine Applikati- on in weiten Teilen plattformunabhängig in JavaScript zu implementieren. Die Abstraktionsschicht kapselt die von der nativen Plattform zur Verfügung gestellten APIs und erlaubt es dem Entwickler, sie über eine einheitliche Schnittstelle anzusprechen (Abb. 1). Im Idealfall sieht das dann aus wie in Listing 1. Mittels Ti.UI.createButton wird ein Button erzeugt, der beim An- tippen einen Dialog öffnet. In Abbildung 2 und 3 ist das Resultat auf Android bzw. iOS zu sehen. Wie deutlich zu sehen ist, werden die jeweiligen nativen UI-Elemente der Plattform verwendet. Dies ist ein großer Unterschied zu Ansätzen, die HTML zur Erzeugung der Oberfläche verwenden (z. B. jQuery Mobile oder Sencha Touch) und dann PhoneGap (bzw. Cordova) zur Integration in die native Plattform nutzen. Ob das Versprechen „Write once, run everywhere“ mit Titanium Wirklichkeit wird, wird sich im Lauf des Artikels noch zeigen. Ein Beispiel Um die Möglichkeiten von Titanium besser beurteilen zu können, implementieren wir im Rahmen dieses Artikels eine kleine Anwendung, die das Stöbern in einer Musik- sammlung und das Entdecken von neuer Musik ermög- lichen soll. Dazu bietet die App dem Anwender folgende Möglichkeiten: 1. Suche nach allen Alben eines Künstlers 2. Auflistung der Tracks auf einem Album Wer träumt nicht davon, mit der eigenen App reich und berühmt zu werden? Angesichts der aktuellen Verkaufszahlen von Android-Devices möchte man natürlich vom ersteigenden Marktanteil dieser Platt- form profitieren, jedoch nicht auf die zahlungsfreudigen iOS-Nutzer verzichten. Ein Dilemma? Cross- Plattform-Toolkits wie Appcelerator Titanium versprechen die Lösung des Problems: Write once, run everywhere. Cross-Plattform-Entwicklung mit Titanium Now playing: TITANIUM Projekt auf GitHub: http://bit.ly/VLVPIl Image licensed by Ingram Image © iStockphoto.com/aleksandarvelasevic

description

Wer träumt nicht davon, mit der eigenen App reich und berühmt zu werden? Angesichts der aktuellen Verkaufszahlen von Android-Devices möchte man natürlich vom ersteigenden Marktanteil dieser Plattform profitieren, jedoch nicht auf die zahlungsfreudigen iOS-Nutzer verzichten. Ein Dilemma? Cross-Plattform-Toolkits wie Appcelerator Titanium versprechen die Lösung des Problems: Write once, run everywhere.

Transcript of Now playing Titanium (Mobile Technology 2013)

Page 1: Now playing Titanium (Mobile Technology 2013)

www.mobile360.deMobile Technology 1 | 2013

086 Mobile Web | Titanium

von Peter Friese

Titanium Mobile von Appcelerator ist ein SDK zur platt-formübergreifenden Entwicklung von Apps für mobile Geräte. Derzeit werden die beiden Plattformen iOS und Android unterstützt, der Support für BlackBerry (auf Ba-sis von BB 10) be� ndet sich in der Entwicklung [1]. Eine native Unterstützung von Windows Phone wird immer mal wieder diskutiert, ist derzeit aber nicht verfügbar. Titanium verfolgt einen hybriden Ansatz, d. h. Teile der Applikation werden als nativer Code ausgeführt, wäh-rend andere Teile in JavaScript implementiert werden. Dazu stellt Titanium eine Abstraktionsschicht zur Ver-fügung, die es dem Entwickler erlaubt, seine Applikati-on in weiten Teilen plattformunabhängig in JavaScript zu implementieren. Die Abstraktionsschicht kapselt die von der nativen Plattform zur Verfügung gestellten APIs und erlaubt es dem Entwickler, sie über eine einheitliche Schnittstelle anzusprechen (Abb. 1).

Im Idealfall sieht das dann aus wie in Listing 1. Mittels Ti.UI.createButton wird ein Button erzeugt, der beim An-

tippen einen Dialog öffnet. In Abbildung 2 und 3 ist das Resultat auf Android bzw. iOS zu sehen. Wie deutlich zu sehen ist, werden die jeweiligen nativen UI-Elemente der Plattform verwendet. Dies ist ein großer Unterschied zu Ansätzen, die HTML zur Erzeugung der Ober� äche verwenden (z. B. jQuery Mobile oder Sencha Touch) und dann PhoneGap (bzw. Cordova) zur Integration in die native Plattform nutzen. Ob das Versprechen „Write once, run everywhere“ mit Titanium Wirklichkeit wird, wird sich im Lauf des Artikels noch zeigen.

Ein BeispielUm die Möglichkeiten von Titanium besser beurteilen zu können, implementieren wir im Rahmen dieses Artikels eine kleine Anwendung, die das Stöbern in einer Musik-sammlung und das Entdecken von neuer Musik ermög-lichen soll. Dazu bietet die App dem Anwender folgende Möglichkeiten:

1. Suche nach allen Alben eines Künstlers2. Au� istung der Tracks auf einem Album

Wer träumt nicht davon, mit der eigenen App reich und berühmt zu werden? Angesichts der aktuellen Verkaufszahlen von Android-Devices möchte man natürlich vom ersteigenden Marktanteil dieser Platt-form pro� tieren, jedoch nicht auf die zahlungsfreudigen iOS-Nutzer verzichten. Ein Dilemma? Cross-Plattform-Toolkits wie Appcelerator Ti tanium versprechen die Lösung des Problems: Write once, run everywhere.

Cross-Plattform-Entwicklung mit Titanium

Now playing: TITANIUM

Projekt auf GitHub: http://bit.ly/VLVPIl

Imag

e lic

ense

d by

Ingr

am Im

age

© iS

tock

phot

o.co

m/a

leks

anda

rvel

asev

ic

ptr
Typewritten Text
FA-274, 2013
Page 2: Now playing Titanium (Mobile Technology 2013)

www.mobile360.de 1 | 2013 Mobile Technology

Titanium | Mobile Web 087

3. Reinhören in einen Track4. Suche nach den Alben eines Künstlers anhand des

Barcodes einer vorliegenden CD des Künstlers

In Abbildung 4 ist der Screen Flow der Applikation an-hand einiger Screen Mock-ups dargestellt.

Obwohl die App natürlich einen gewissen Nutzwert mit sich bringt, steht die Beurteilung der folgenden As-pekte im Vordergrund:

• Wie werden Oberfl ächen programmiert?• Wie gut kapselt Titanium die nativen APIs – kann die

App tatsächlich plattformagnostisch entwickelt wer-den oder müssen plattformspezifi sche Anpassungen vorgenommen werden?

• Wie erfolgt der Zugriff auf Daten aus dem Internet?• Wie erfolgt der Zugriff auf native APIs (in unserem

Fall der Mediaplayer)?• Wie erfolgt der Zugriff auf die Kamera, bzw. wie

können Third-Party-Module eingebunden werden (in unserem Fall ein Barcodescanner)?

• Wie kann das Aussehen der App angepasst werden (Theming/Styling)?

Projekt anlegenAls Grundlage für unser Projekt benutzen wir das Master/Detail Application Template, da es bereits die grundle-gende Struktur für ein Multi-Plattform-Projekt mitbringt. Beim Anlegen des Projekts reicht es aus, als Target iPhone und Android auszuwählen, die Unterstützung für die App celerator Cloud kann deaktiviert werden.

Bevor wir mit der Implementierung beginnen, lohnt sich ein kurzer Blick auf die Projektstruktur, um sich einen Überblick zu verschaffen. In Abbildung 5 ist die allgemeine Projektstruktur zu sehen. Unterhalb des /Resources/-Ordners befi nden sich zwei Ordner für die Grafi kdateien für das jeweilige Betriebssystem, z. B. ent-hält /Resources/iphone/Default.png den Splash Screen für

Abb. 1: Titanium-Architektur

Listing 1var button = Ti.UI.createButton({ title: 'Click me', top: 10});

self.add(button);

//Add behavior for UIbutton.addEventListener('click', function(e) { var dialog = Ti.UI.createAlertDialog({ cancel: 1, buttonNames: ['Great', 'Fine', 'Not too bad'], message: 'How are you today?', title: 'Hello' }).show();});

Non-Retina iPhones und /Re­sources/iphone/[email protected] den Splash Screen für Re-tina iPhones. Unterhalb von /Resources/ui/ befi nden sich JavaScript-Dateien für das UI der App. Im Ordner /Resour­ces/ui/common/ befi nden sich dabei die JavaScript-Dateien, die auf allen Plattformen verwendet werden sollen, während unter /Resources/ui/handheld/android/ bzw. /Resources/ui/handheld/ios/ jeweils JavaScript-Dateien für die Verwendung auf einer be-stimmten Plattform zu fi nden sind. In der Datei ApplicationWindow.js, zu fi nden jeweils für Android, iOS sowie für Tablet (hierbei sind sowohl iPad als auch Android-Tablets gemeint), wird das allge-meine UI-Layout für die App festgelegt. Welches Layout verwendet wird, wird in der Startdatei app.js defi niert. Ein schneller Blick zeigt, dass dort anhand der Bildschirmauf-lösung und des Betriebssystems entschieden wird, welches Layout verwendet werden soll. Für Android-Smartphones mit hochaufl ösendem Display wie z. B. dem Galaxy Ne-xus liefert dieser Check jedoch fälschlicherweise das Er-gebnis, das Gerät sei ein Tablet, daher entfernen wir die Tablet-Erkennung fürs erste.

In der Datei /Resources/ui/common/MasterView.js wird eine einfache View aufgebaut, die eine Liste mit Früchten enthält. Wenn der Benutzer auf einen der Einträge tippt, wird ein Detailbildschirm aufgerufen (/Resources/ui/common/DetailView.js), der den Preis des Obstes anzeigt. Führen Sie die Applikation aus, in-dem Sie im App Explorer das mit einem grünen Pfeil versehene Run-Menü aufklappen und die Applikation

Abb. 2: Einfacher Dialog auf Android

Abb. 3: Der gleiche Dialog unter iOS

Page 3: Now playing Titanium (Mobile Technology 2013)

088 Mobile Web | Titanium

www.mobile360.deMobile Technology 1 | 2013

entweder auf dem iOS-Simulator oder auf dem An-droid-Emulator ausführen (Abb. 6).

Auf Basis dieser Grundlage wollen wir nun eine Liste implementieren, welche die Alben eines Künstlers anzeigt.

Darstellung von Daten in einer ListeSuche und Darstellung der Ergebnisse sollen in einem gemeinsamen Screen erfolgen, der neben einer Liste zur Darstellung der Ergebnisse auch ein Textfeld zur Einga-be der Suche enthalten soll. Analog zur Datei Master-View.js legen wir eine Datei SearchView.js an, die die neue View aufnehmen soll. Listing 2 zeigt die Grund-struktur für eine View mit Liste.

Damit die View beim Start der Anwendung angezeigt wird, müssen wir sie noch in ApplicationWindow.js re-gistrieren. Listing 3 zeigt den Vorgang für iOS.

Nachdem wir nun also die grundlegende Struktur geschaffen haben, können wir uns der Beschaffung der

Daten widmen. Es gibt etliche Dienste, die eine Suche nach Musikmetadaten ermögli-

chen. Für unsere Zwecke eignet sich das iTunes Af� liate Search API [2], das die benötigten Daten als JSON be-reitstellt.

Um Daten über HTTP zu laden, können wir die Klasse Titanium.Network.HTTPClient benutzen [3], die ein asynchrones Senden von HTTP Requests er-laubt. Um die asynchron eintreffenden Daten zu ver-arbeiten, wird der HTTPClient mit zwei Callbacks kon� guriert: onload(event) dient der Verarbeitung der Daten im Erfolgsfall, onerror(event) wird im Fehler-fall aufgerufen. Im Erfolgsfall können die empfange-nen Daten im Attribut responseText des HTTPClients abgefragt werden, im Fehlerfall enthält der Parameter event ein Attribut error. Wie ein einfacher HTTP Re-quest aufgebaut und abgesendet werden kann, ist in Listing 4 zu sehen.

Für unsere Zwecke muss dieser Code noch um zwei Aspekte angepasst werden: Einerseits muss der URL mit einem Such-String parametrisiert werden, andererseits sollen die Ergebnisdaten natürlich nicht auf der Konsole ausgegeben, sondern in einer Liste dargestellt werden.

Wir ergänzen die Klasse SearchView also um die Me-thoden loadData(searchTerm), get Search Url(search-Term) und get DataArray(json) (Listing 5). Die Methode loadData entspricht dabei weitestgehend dem Beispiel von eben, ruft aber zunächst die Methode getSearchUrl auf, um anhand des Suchbegriffs den korrekten URL zu ermitteln. Wenn dann die Daten geladen wurden, wer-den sie zunächst mittels JSON.parse von Text in eine JSON-Objektstruktur umgewandelt. Anschließend wird die Methode createCells aufgerufen, welche die im JSON-Objekt enthaltenen Daten zur Anzeige bringt. Dazu bedient sie sich zunächst der Hilfsmethode get-DataArray, die innerhalb des JSON-Objekts das für uns relevante Array herauspickt. Anschließend werden die einzelnen Elemente dieses Arrays an die Methode createRow gesendet, die für jedes Element eine Tabel-lenzelle erzeugt. Abschließend werden diese Tabellen-zellen per table.data = data in die Liste übertragen.

Die Erzeugung der Zellen selbst ist recht einfach: Mittels Ti.UI.createTableViewCell erzeugen wir eine

Abb. 4: Mock-up der Applikation

Listing 2

function SearchView() { var self = Ti.UI.createView({ backgroundColor: 'white' });

var table = Ti.UI.createTableView(); self.add(table);

return self;}

module.exports = SearchView;

Abb. 5: ProjektstrukturAbb. 6: Master/Detail-App im iOS-Simulator

Page 4: Now playing Titanium (Mobile Technology 2013)

Titanium | Mobile Web 089

www.mobile360.de 1 | 2013 Mobile Technology

neue Zelle und setzen die Eigenschaften title und left-Image.

Um den bisherigen Stand schon mal beurteilen zu können, fügen wir noch einen Dummy-Aufruf für die Suche am Ende der Klasse (vor return self;) ein: loadData('Madonna');. Das Ergebnis kann sich sowohl auf iOS als auch unter Android bereits sehen lassen (Abb. 7 und 8).

Search For the Hero Inside Your HeartSo weit, so gut. Nun müssen wir noch die Suche ein-binden. Dazu benötigen wir ein Suchfeld, das mit der Tabelle gekoppelt ist. Titanium stellt dazu die Klasse

Ti.UI.SearchBar zur Verfügung, die wir dem Attribut search der Tabelle zuweisen. Sobald der Benutzer mehr als ein Zeichen in das Suchfeld eingegeben hat, soll die Suche starten, also fügen wir der Searchbar einen Event

Listing 4

var url = "http://www.appcelerator.com";var client = Ti.Network.createHTTPClient({ // function called when the response data is available onload : function(e) { Ti.API.info("Received text: " + this.responseText); alert('success'); }, // function called when an error occurs, including a timeout onerror : function(e) { Ti.API.debug(e.error); alert('error'); }, timeout : 5000 // in milliseconds});// Prepare the connection.client.open("GET", url);// Send the request.client.send();

Listing 5

var loadData = function(searchTerm) { var client = Ti.Network.createHTTPClient({ onload: function(e) { var json = JSON.parse(this.responseText); createCells(json); }, timeout: 5000 });

var url = getSearchUrl(searchTerm) client.open('GET', url); client.send();}

var createCells = function(json) { var data = []; var dataArray = getDataArray(json); for (var i = 0; i < dataArray.length; i++) { var row = createRow(dataArray[i]); data.push(row); } table.data = data;}

var createRow = function(dataElement) { var collectionId = dataElement.collectionId; var collectionName = dataElement.collectionName; var artistName = dataElement.artistName; var imageUrl = dataElement.artworkUrl60; var row = Ti.UI.createTableViewRow({ title: collectionName, leftImage: imageUrl, artistName: artistName, collectionId: collectionId, album: dataElement }); return row;}

var getDataArray = function(json) { return json.results;}

var getSearchUrl = function(searchTerm) { return "http://itunes.apple.com/search?term=" + searchTerm + "&limit=30&country=DE" + "&entity=album&attribute=artistTerm";}

Listing 3

function ApplicationWindow() { var SearchView = require('ui/common/SearchView'); var self = Ti.UI.createWindow({ backgroundColor:'#ffffff' });

var searchView = new SearchView(); var searchViewContainerWindow = Ti.UI.createWindow({ title: 'MusiXplorer' }); searchViewContainerWindow.add(searchView); var navGroup = Ti.UI.iPhone.createNavigationGroup({ window: searchViewContainerWindow }); self.add(navGroup);

return self;};module.exports = ApplicationWindow;

Page 5: Now playing Titanium (Mobile Technology 2013)

090 Mobile Web | Titanium

www.mobile360.deMobile Technology 1 | 2013

Listener hinzu, der die Methode loadData aufruft (Lis-ting 6).

Besondere Erwähnung verdient in diesem Zusammen-hang das Attribut � lterAttribute der Tabelle. Es dient

dazu, diejenigen Zeilen zu � ltern, die den Suchtext in einem ihrer Attribute enthalten. Normalerweise wird hier der Titel der Zellen verwendet. Da aber der Name des Künstlers in vielen Fällen nicht im Titel des Albums vorkommt, müssen wir hier einen kleinen Trick anwen-den. Zunächst setzen wir das Attribut � lterAttribute der Tabelle auf den Wert artistName. Außerdem sorgen wir dafür, dass beim Erzeugen der Tabellenzellen jede Zelle ein zusätzliches Attribut artistName erhält, welches mit dem Namen des Künstlers des Albums besetzt wird. Das aktualisierte Listing 7 mit der Methode createRow veran-schaulicht dies.

Natürlich dürfen wir nicht vergessen, den Dummy-Aufruf der Suche nach Madonna zu entfernen – ansons-ten erscheint beim Start der Anwendung immer erst mal die Liste ihrer Alben.

Navigation zwischen ScreensWie in Abbildung 4 zu sehen ist, soll beim Tippen auf eine Zelle eine Drill-down-Navigation zu dem entspre-chenden Album des Künstlers erfolgen. Dazu müssen folgende Dinge getan werden:

•Erstellen eines Screens für die Darstellung von Alben•Event Handler für das click-Event auf der Tabelle

registrieren•Versenden des Events albumSelected sobald das click-

Event aufgetreten ist•Event Handler für das Abfangen des albumSelected-

Events auf der View für Alben registrieren

Der Screen für die Darstellung von Alben ist dem ersten Screen recht ähnlich, daher gehen wir hier nicht weiter auf seine Erstellung ein. Der gesamte Quellcode der App ist übrigens auf GitHub unter [4] verfügbar, der Code für den Screen zur Albendarstellung ist in der Klasse Album-WithTracksView zu � nden. Der Event Handler für das Reagieren auf einen Tipp auf eine Tabellenzelle sieht wie folgt aus:

table.addEventListener('click', function(event) { self.fi reEvent('albumSelected', { data: event.rowData });});

Das Event enthält die Daten der ausgewählten Zelle (event.rowData), damit der Empfänger des Events aus-werten kann, auf welches Album der Benutzer geklickt hat – zu diesem Zweck müssen die Tabellenzellen zu-sätzlich ein Attribut für die collectionId erhalten.

Dieses Event wird nun auf Applikationsebene aufge-fangen, um die Navigation zu steuern. Der Applikations-Controller sendet das Event einfach an den nächsten in der Navigationshierarchie be� ndlichen Screen weiter und sorgt dafür, dass dieser Screen dargestellt wird. Unter iOS wird dazu die View in der NavigationGroup geöffnet:

Listing 6var searchBar = Ti.UI.createSearchBar({ hintText: 'Search'});searchBar.addEventListener('change', function(event) { searchTerm = searchBar.value; if (searchTerm.length > 1) { loadData(searchTerm); }});

var table = Ti.UI.createTableView({ search: searchBar, fi lterAttribute: 'artistName'});self.add(table);

Listing 7

var createRow = function(dataElement) { var collectionName = dataElement.collectionName; var artistName = dataElement.artistName; var imageUrl = dataElement.artworkUrl60;

var row = Ti.UI.createTableViewRow({ title: collectionName, artistName: artistName, leftImage: imageUrl }); return row;}

Listener hinzu, der die Methode

Abb. 7: Ergebnisliste unter iOS Abb. 8: Ergebnisliste unter Android

Page 6: Now playing Titanium (Mobile Technology 2013)

Titanium | Mobile Web 091

www.mobile360.de 1 | 2013 Mobile Technology

searchView.addEventListener('albumSelected', function(event) { albumWithTracksView.fireEvent('albumSelected', event); navGroup.open(albumWithTracksWindow);});

Für Android müssen wir anders vorgehen – hier gibt es keine NavigationGroup. Stattdessen lauschen wir – wie auch unter iOS – auf das Event albumSelected. Sobald dieses Event auftritt, bauen wir ein zweites Fenster auf, erzeugen eine neue Instanz des AlbumWithTracksView, fügen diese dem neuen Fenster hinzu und öffnen das neue Fenster (Listing 8).

Zu guter Letzt muss das so versendete Event im View AlbumWithTracksView abgefangen werden. Die im Event enthaltenen Daten werden sodann benutzt, um die Tabelle der Tracks auf dem ausgewählten Album zu aktualisieren:

self.addEventListener('albumSelected', function(event) { loadData(event.data.album);});

Let The Music PlayUm in einen Song reinzuhören, implementieren wir nun noch einen dritten Screen, PlaySongView, der das Cover des Albums anzeigt, auf dem der Song enthalten ist. Mit einem unterhalb des Covers angeordneten Button kann der Benutzer dann den Song starten und stoppen. Die Na-vigation zu dem Screen erfolgt mittels des nun bekannten Event-basierten Schemas (der Name des Events lautet diesmal trackSelected).

Das Abspielen des Songs erfolgt mithilfe des Audioplay-ers, den Titanium unter dem Namen Ti.Media.Audio-Player zur Verfügung stellt. Über die Eigenschaft url kann eine Referenz auf einen MP3-Stream oder eine Audioda-tei übergeben werden, die Wiedergabe lässt sich mittels der Methoden start() und stop() steuern. Ob gerade ein Song wiedergegeben wird, lässt sich mittels playing() bzw. paused() ermitteln. Das ist wichtig, um beim Abspielen ei-nes neuen Songs den Player zuerst kurz anzuhalten. Der gesamte Ablauf ist in Listing 9 noch einmal zusammen-gefasst.

Barcodes scannenAls zusätzliche Funktionalität soll nun noch anhand des Barcodes einer vorhandenen CD der Künstler er-mittelt werden, anschließend kann der Benutzer dann wie gehabt die Alben dieses Künstlers einsehen. Der Barcodescanner ersetzt also lediglich die Eingabe des Künstlernamens.

Um Barcodes zu scannen, benötigen wir Zugriff auf die Kamera und müssen das eingelesene Videosignal nach Barcodes durchsuchen. Die Implementierung eines Bildanalysealgorithmus würde den Rahmen des Artikels wohl etwas sprengen, doch glücklicherweise können wir auf eine existierende Komponente zurückgreifen. App cel-erator stellt unter [5] einen Marktplatz für Komponenten zur Verfügung, der über eine große Auswahl an Kompo-

nenten für die unterschiedlichsten Zwecke verfügt. Eine Suche nach Barcode fördert einige Komponenten zu Tage. Für die vorliegende App entscheiden wir uns für den Bar-codescanner der Firma Scandit [6], da sie über eine Com-munityvariante verfügt, die für kostenlose Apps (oder für Demos) kostenfrei einsetzbar ist. Um diese Komponente zu verwenden, benötigt man einen App-Key, den man

Listing 8

searchView.addEventListener('albumSelected', function(event) { // Navgigate to Album Tracks View var albumWithTracksView = new AlbumWithTracksView(); var albumWithTracksWindow = Ti.UI.createWindow({ title: 'Tracks', navBarHidden: false, backgroundColor: '#ffffff' }); albumWithTracksWindow.add(albumWithTracksView); albumWithTracksView.fireEvent('albumSelected', event); albumWithTracksWindow.open();});

Listing 9

var audioPlayer = Ti.Media.createAudioPlayer({ allowBackground: true});var stopPlaying = function() { if (audioPlayer.playing || audioPlayer.paused) { audioPlayer.stop(); if (Ti.Platform.name === 'android') { audioPlayer.release(); } }}var togglePlaying = function() { if (audioPlayer.playing || audioPlayer.paused) { audioPlayer.stop(); if (Ti.Platform.name === 'android') { audioPlayer.release(); } } else { audioPlayer.start(); }}var loadData = function(track) { var previewUrl = track.previewUrl; var imageUrl = track.artworkUrl100;

stopPlaying(); audioPlayer.url = previewUrl; albumImage.url = imageUrl; togglePlaying();}

Page 7: Now playing Titanium (Mobile Technology 2013)

092 Mobile Web | Titanium

www.mobile360.deMobile Technology 1 | 2013

nach Registrierung unter [7] erhält. Die Erkennung des Barcodes wird von einer kleinen nativen Bibliothek über-nommen, die für Android und iOS separat implementiert wurde und im Modul im Verzeichnis für das jeweilige Be-triebssystem zu �nden ist.

Die Installation der Komponente in unsere App erfor-dert den Download der Komponente vom Appcelerator Marketplace. Nach einem Klick auf den Download-But-ton auf der Komponentenseite wird die Komponente zu-nächst dem Konto des eigenen Appcelerator-Benutzers zugewiesen. Auf der Seite „Purchased Products“ kann die Komponente dann als ZIP-Archiv heruntergeladen werden. Nach dem Download müssen wir das Archiv entpacken und den dadurch entstandenen /modules/-Ordner in das Wurzelverzeichnis des Projekts kopieren, sodass der Ordner parallel zum /Resources/-Ordner liegt. Um die Komponente nun in unserem Projekt zu

registrieren, öffnen wir die Datei tiapp.xml und klicken auf den grünen Plus-Button neben der Modulliste. Der nun erscheinende Dialog sollte unter anderem das Mo-dul com.mirasense.scanditsdk au�isten (Abb.  9). Per Doppelklick auf den Eintrag wird das Modul in die Modulliste des Projekts übernommen und steht ab so-fort zur Verfügung.

Die Verwendung des Barcodescanners in einer eige-nen View ist relativ einfach. Zunächst muss das Mo-dul mittels var scanditsdk = require("com.mirasense.scanditsdk"); geladen werden, anschließend kann mit scanditsdk.createView die Scanner-View erzeugt und anschließend zu einer umgebenden View hinzugefügt werden. Der Barcodescanner verfügt über die beiden Callbacks successCallback und cancelCallback, über die die Benutzer interaktion mitgeteilt wird. Wenn der Benutzer einen Barcode gescannt hat, wird über den Callback successCallBack sowohl der Barcode als auch die Symbologie (z. B. EAN oder UPC) mitgeteilt. Die-sen Code können wir benutzen, um über einen geeigne-ten Web Service die Metadaten des gescannten Albums zu beziehen und den Künstler auszuwerten. Zwar un-terstützt das iTunes Af�liate Search API angeblich die Suche nach EAN- und UPC-Codes, doch leider funkti-onierte dies für den Großteil der probehalber gescann-ten CDs nicht. Glücklicherweise herrscht kein Mangel an alternativen Web Services. Die von uns benötig-ten Informationen werden z. B. von MusicBrainz zur Verfügung gestellt, der passende Aufruf lautet http://search.musicbrainz.org/ws/2/release/?fmt=json&query

Listing 11

// hook up scanner view to right buton on search viewvar openScannerButton = Ti.UI.createButton({ title: 'Scan'});openScannerButton.addEventListener('click', function() { var scannerWindow = Ti.UI.createWindow({title: 'Scan'}); closeButton = Ti.UI.createButton({ title: 'Close', style: Ti.UI.iPhone.SystemButtonStyle.PLAIN }); closeButton.addEventListener('click', function() { scannerWindow.close(); }); scannerWindow.leftNavButton = closeButton; var scannerView = new ScannerView(); scannerView.addEventListener('artistScanned', function(event) { searchView.fireEvent('artistScanned', event); scannerWindow.close(); }); scannerWindow.add(scannerView); scannerWindow.open({modal: true});});searchViewContainerWindow.rightNavButton = openScannerButton;

Listing 10var scanditsdk = require("com.mirasense.scanditsdk");var picker = scanditsdk.createView({ "width": Ti.UI.FILL, // Ti.Platform.displayCaps.platformWidth, "height": Ti.UI.FILL // Ti.Platform.displayCaps.platformHeight});self.add(picker);picker.init("...hier kommt der Lizenzschlüssel rein...", 0);picker.setSuccessCallback(function(e) { retrieveAlbum(e.symbology, e.barcode);});picker.setCancelCallback(function(e) { alert("canceled");});picker.startScanning();var retrieveAlbum = function(symbology, barcode) { var client = Ti.Network.createHTTPClient({ onload: function(e) { var json = JSON.parse(this.responseText); var releases = getDataArray(json); var namecredit = releases[0]['artist-credit']['name-credit'][0]; var artist = namecredit.artist.name; self.fireEvent('artistScanned', { artist: artist }); }, timeout: 5000 }); var url = getSearchUrl(barcode) client.open('GET', url); client.send();}var getDataArray = function(json) { var result = json['release-list'].release; return result;}var getSearchUrl = function(searchTerm) { return "http://search.musicbrainz.org/ws/2/release/?fmt=json&query=barcode:" + searchTerm;}

Page 8: Now playing Titanium (Mobile Technology 2013)

Titanium | Mobile Web 093

www.mobile360.de 1 | 2013 Mobile Technology

=barcode:<hier kommt der Barcode> und liefert Daten zu einem Release (d. h. einem Album). In Listing 10 sehen Sie die relevanten Teile des Codes.

Die Barcode-View muss nun natürlich noch an pas-sender Stelle eingebunden werden. Dazu erzeugen wir einen neuen Toolbar-Button, den wir in der Titelleiste des Suchdialogs einklinken (Listing 11).

Für Android müssen wir anders vorgehen – hier haben wir keine Navigationsleiste. Stattdessen registrieren wir einen Menüeintrag, der beim Drücken der Menütaste (auf alten Android-Devices) bzw. Tippen auf den Menü-Softbutton (auf neuen Devices) erscheint (Listing 12).

Abschließend muss der Suchdialog noch einen Event Handler für das Event artistScanner erhalten, damit der Name des Künstlers in das Suchfeld übertragen werden kann:

self.addEventListener('artistScanned', function(event) { searchBar.value = event.artist; loadData(event.artist);});

Da der iPhone-Simulator über keine Kamera verfügt, müssen wir die App zum Ausprobieren nun auf ein ech-tes Gerät deployen. Im App Explorer gibt es dazu im Run-Menü den Eintrag _OS Device. Ein Wizard führt uns nun durch die notwendigen Schritte, um die Appli-kation zu signieren und über iTunes auf das Gerät zu deployen. Grundvoraussetzung ist natürlich eine Ap-ple-iOS-Developer-Program-Mitgliedschaft sowie ein Provisioning Pro�le, das Sie über das iOS Provisioning Portal [8] anlegen müssen.

Hübschere ZellenFunktional ist unsere App somit vollständig, allerdings lässt die Darstellung der Tabellenzeilen noch etwas zu wünschen übrig. Unter Android werden bereits die Al-bumcover angezeigt, unter iOS jedoch nicht, auch eine Information über den Künstler wäre sicherlich nicht schlecht. Früher oder später wird für jede App der Mo-ment kommen, an dem die Standard-Layouts für Tabel-lenzellen nicht mehr ausreichen und man eigene Layouts implementieren muss. Die Erstellung von maßgeschnei-derten Tabellenzellen erfolgt durch eine geschickte Kombination von TableViews, Table View Rows, Views, Images und Labels. Titanium verfügt über ein Layout-System, das die Erstellung von relativen Layouts erleich-tert und uns das Hantieren mit absoluten Positionen erspart [9].

Als Beispiel implementieren wir eine Tabellenzelle für die Suchmaske. Neben dem Albumcover soll der Albumtitel sowie der Künstlername dargestellt wer-den. Um dem Benutzer zu verdeutlichen, dass sich hinter jeder Zelle noch mehr Daten verbergen, soll da-rüber hinaus ein Disclosure Indicator angezeigt werden (Abb. 10).

Die Tabellenzelle selbst erzeugen wir wie gewohnt mit Ti.UI.createTableViewRow. Indem wir das At-

tribut has Child auf true setzen, wird auto-matisch ein Disclosure Indicator angezeigt. Einen Platzhalter für das Albumcover erzeu-gen wir mit Ti.UI.createImageView – auch hier können wir wie schon bekannt einfach den URL zu dem gewünschten Bild ange-ben und Titanium kümmert sich um den asynchronen Download der Bilddaten. Die beiden Labels fügen wir nicht direkt auf der Tabellenzelle ein, sondern erzeu-gen zunächst mittels Ti.UI.createView eine Container-View, die die beiden Labels aufnehmen wird. Grund hierfür ist, dass wir so die Layout-Orientierung für die Labels auf vertical setzen können, während sie für die Zelle selbst auf horizontal stehen muss. Den vollstän-digen Code für das Erstellen der Zelle �nden Sie auf GitHub.

Listing 12

var activity = self.activity;activity.onCreateOptionsMenu = function(event) { var menu = event.menu; var menuItem = menu.add({ title: "Scan" }); menuItem.icon = "images/glyphicons_179_eject.png"; menuItem.addEventListener("click", function(event) { var scannerWindow = Ti.UI.createWindow({title: 'Scan'}); var scannerView = new ScannerView(); scannerView.addEventListener('artistScanned', function(event) { searchView.fireEvent('artistScanned', event); scannerWindow.close(); }); scannerWindow.add(scannerView); scannerWindow.open({modal: true}); });};

Abb. 9: Modul zum Projekt hinzufügen

Abb. 10: Design einer erweiterten Tabellen-zelle

Page 9: Now playing Titanium (Mobile Technology 2013)

094 Mobile Web | Titanium

www.mobile360.deMobile Technology 1 | 2013

Write once, run everywhere?Abschließend wollen wir einen Blick auf die am Anfang des Artikels aufgestellten Fragenkomplexe werfen. Ganz allgemein kann festgehalten werden, dass die Implemen-tierung von Apps mittels Titanium �ott von der Hand geht. Viele Dinge, die man bei nativer Programmierung unter Android oder iOS aufwändig selbst implementie-ren müsste, bekommt man bei Titanium oft geschenkt (z. B. einfacher asynchroner Zugriff auf Web Services).

Die Implementierung von Ober�ächen erfolgt pro-grammatisch über die von Titanium bereitgestellte Ab-straktionsschicht, die native UI-Elemente erzeugt. Dies führt zu einem nativen Look and Feel, was ja durchaus gewünscht ist. Für viele UI-Elemente funktioniert die Ab-straktionsschicht sehr gut, sodass tatsächlich große Teile der Anwendung plattformagnostisch entwickelt werden können. An einigen Stellen geht das nicht – in unserem Beispiel haben wir gesehen, welche Möglichkeiten es gibt, mit dieser Situation umzugehen. Unter Android mussten wir z. B. auf die aktuelle Activity zugreifen, um ein Menü zu registrieren – ganz ohne Kenntnis der plattformspezi-�schen Konzepte kommt man bei der Entwicklung von Titanium-Apps also nicht aus. Auch bei den Navigations-konzepten sind bei den beiden unterstützten Plattformen so deutliche Unterschiede zu verzeichnen, dass wir in un-serer App darauf eingehen mussten.

Der Zugriff auf Daten aus dem Internet erfolgt über eine einfach zu benutzende Klasse. Dank der in JavaScript omnipräsenten Callbacks ist die Verarbeitung von asyn-chronen Aufrufen ziemlich einfach, sodass dieser Teil der Anwendung erstaunlich trivial zu implementieren ist.

Der Zugriff auf die nativen APIs und hardwarenahen Funktionen von Smartphones wie z. B. die Kamera oder den Mediaplayer wird von Titanium bzw. den von an-deren Herstellern angebotenen Komponenten gut gelöst und funktioniert reibungslos. Da leider noch nicht alle APIs von Titanium selbst gekapselt sind (z. B. fehlt un-ter Android der Zugang zur lokalen Medienbibliothek), so bieten eben diese Module eine Möglichkeit, fehlende Funktionalitäten nachzurüsten. Es darf dabei allerdings nicht vergessen werden, dass zur Implementierung eines Titanium-Moduls native Komponenten programmiert werden müssen. Einen guten Überblick über die dazu notwendigen Schritte gibt [10].

Das SDK weiß also zu gefallen. Weniger angenehm ist die Arbeit mit der IDE, denn obwohl Appcelerator sich vor einiger Zeit mit Aptana Studio eine Eclipse-basierte IDE eingekauft und sie speziell für die Entwicklung von Titanium-Apps zugeschnitten hat, sind die damit zu er-zielenden Roundtrip-Zeiten deutlich langsamer als bei nativer Entwicklung auf der jeweiligen Plattform. Am zügigsten ist noch das Deployment auf den iOS-Simu-lator – bei einfachen Projekten ist die Wartezeit für das Neukompilieren der App nur unwesentlich länger als bei der Programmierung einer nativen App unter Xcode. Das Deployment auf ein Testgerät ist jedoch durch den Umweg über iTunes (zunächst wird ein IPA-Archiv er-zeugt und dann mittels iTunes auf das Device synchro-

nisiert) schon deutlich langsamer und umständlicher. Leider ist mit der aktuellen Version auch kein On-Device Debugging möglich. Dienste wie ClouDebug [11] bieten hierfür Lösungen an, sind jedoch auf eine Verbindung mit dem Internet angewiesen. Ab Version 3 steht für iOS ein On-Device Debugging über das lokale WLAN zur Verfügung [12]. Das geschlossene System von Apple (mit undokumentierten Schnittstellen für Deployment und Debugging) macht es Herstellern wie Appcelerator erkennbar schwer, eine nahtlose Integration herzustel-len. Unter Android sieht es ganz ähnlich aus, auch hier wird es ab Titanium 3 eine Möglichkeit zum On-Device Debugging geben. Derzeit ist Live-Debugging also nur auf den Emulatoren/Simulatoren möglich. Während der iOS-Simulator wie bereits erwähnt seht �ott zu Werke geht und somit die zu debuggende Applikation auch schnell gestartet ist, dauert alleine das Starten der Appli-kation auf dem Android-Emulator eine gefühlte Ewig-keit (vom Start des Emulators ganz zu schweigen, aber dies ist ein Problem, das man auch bei nativer Program-mierung unter Android hat).

Alles in allem scheinen die Vorteile sich mit den Nach-teilen die Waage zu halten. Teams, die kein Know-how in Java und/oder Objective-C haben, dafür aber über JavaScript-Wissen verfügen, sind mit Titanium sicherlich gut beraten. Wer bereits über Erfahrungen in der nativen Programmierung für eine Plattform verfügt, sollte sich al-lerdings überlegen, ob er nicht noch eine weitere native Plattform erlernt.

Peter Friese arbeitet als Software Architect und Principal Consul-tant für Zühlke Engineering. Seine Schwerpunkte liegen auf der Entwicklung von plattformübergreifenden mobilen Lösungen (u. a. für iOS, Android, Windows Phone) sowie der modellgetriebenen Entwicklung (MDSD). Peter bloggt auf http://www.peterfriese.de.

@peterfriese

Links & Literatur

[1] http://bit.ly/Y8pXzf

[2] http://bit.ly/bGaJt4

[3] http://bit.ly/VZZ6Aq

[4] http://bit.ly/VLVPIl

[5] http://marketplace.appcelerator.com

[6] http://bit.ly/RXbTG0

[7] http://www.scandit.com/pricing/

[8] http://bit.ly/ay2ogv

[9] http://bit.ly/SRsnh7

[10] http://slidesha.re/lVtzKz

[11] http://www.cloudebug.com

[12] http://bit.ly/RXcs2u

3-IN-1-KONFERENZPAKET

Bis 21. Februar 2013

Agile Day For Free und

über 900 Euro sparen!

www.jax.deJAXJAXCON JAX.KONFERENZ

Europas Nr. 1 Konferenz für Enterprise-Technologien und Strategien

22. – 26. April 2013

Rheingoldhalle, MainzExpo: 23. – 25. April 2013

Continuous Delivery

Java Core

Big Data

Software Architecture

Java Enterprise Performance

HTML5 & JavaScript

User Experience

Spring

Android

Agile

Cloud NoSQL

Media-Partner:

Bronze-Partner:

Finance-Day-Partner:

Java-FX-Day-Partner:

BPM-Day-Partner:

Präsentiert von:

Party-Sponsor:

Veranstalter:

Gold-Partner: Silber-Partner:

Java-EE-Day-Partner:

Agile-Day-Partner: