Now playing Titanium (Mobile Technology 2013)
 

Now playing Titanium (Mobile Technology 2013)

on

  • 329 views

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 ...

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.

Statistics

Views

Total Views
329
Views on SlideShare
327
Embed Views
2

Actions

Likes
0
Downloads
0
Comments
0

1 Embed 2

http://www.slideee.com 2

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Now playing Titanium (Mobile Technology 2013) Now playing Titanium (Mobile Technology 2013) Document Transcript

  • Mobile Web | Titanium Projekt auf GitHub: http://bit.ly/VLVPIl Image licensed by Ingram Image Cross-Plattform-Entwicklung mit Titanium Now playing: TITANIUM 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? CrossPlattform-Toolkits wie Appcelerator Titanium versprechen die Lösung des Problems: Write once, run everywhere. von Peter Friese Titanium Mobile von Appcelerator ist ein SDK zur plattformü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 Basis 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ährend andere Teile in JavaScript implementiert werden. Dazu stellt Titanium eine Abstraktionsschicht zur Verfügung, die es dem Entwickler erlaubt, seine Applikation 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- Mobile Technology 1 | 2013 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 Musiksammlung und das Entdecken von neuer Musik ermöglichen 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 FA-274, 2013 www.mobile360.de © iStockphoto.com/aleksandarvelasevic 086
  • Titanium | Mobile Web 3. Reinhören in einen Track 4. 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 anhand einiger Screen Mock-ups dargestellt. Obwohl die App natürlich einen gewissen Nutzwert mit sich bringt, steht die Beurteilung der folgenden Aspekte im Vordergrund: • Wie werden Oberflächen programmiert? • Wie gut kapselt Titanium die nativen APIs – kann die App tatsächlich plattformagnostisch entwickelt werden oder müssen plattformspezifische 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 anlegen Als Grundlage für unser Projekt benutzen wir das Master/ Detail Application Template, da es bereits die grundlegende 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 Appcelerator 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 befinden sich zwei Ordner für die Grafikdateien für das jeweilige Betriebssystem, z. B. enthält /Resources/iphone/Default.png den Splash Screen für 087 Non-Retina iPhones und /Re­ sources/iphone/Default@2x. png den Splash Screen für Retina iPhones. Unterhalb von /Resources/ui/ befinden sich JavaScript-Dateien für das UI der App. Im Ordner /Resour­ ces/ui/common/ befinden 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 bestimmten Plattform zu finden Abb. 1: Titanium-Architektur sind. In der Datei ApplicationWindow.js, zu finden jeweils für Android, iOS sowie für Tablet (hierbei sind sowohl iPad als auch Android-Tablets gemeint), wird das allgemeine UI-Layout für die App festgelegt. Welches Layout verwendet wird, wird in der Startdatei app.js definiert. Ein schneller Blick zeigt, dass dort anhand der Bildschirmauflösung und des Betriebssystems entschieden wird, welches Layout verwendet werden soll. Für Android-Smartphones mit hochauflösendem Display wie z. B. dem Galaxy Nexus liefert dieser Check jedoch fälschlicherweise das Ergebnis, 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, indem Sie im App Explorer das mit einem grünen Pfeil versehene Run-Menü aufklappen und die Applikation Listing 1 var button = Ti.UI.createButton({ title: 'Click me', top: 10 }); self.add(button); //Add behavior for UI button.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(); }); Abb. 2: Einfacher Dialog auf Android www.mobile360.de Abb. 3: Der gleiche Dialog unter iOS 1 | 2013 Mobile Technology
  • 088 Mobile Web | Titanium Abb. 4: Mock-up der Applikation entweder auf dem iOS-Simulator oder auf dem Android-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 Liste Suche und Darstellung der Ergebnisse sollen in einem gemeinsamen Screen erfolgen, der neben einer Liste zur Darstellung der Ergebnisse auch ein Textfeld zur Eingabe der Suche enthalten soll. Analog zur Datei MasterView.js legen wir eine Datei SearchView.js an, die die neue View aufnehmen soll. Listing 2 zeigt die Grundstruktur für eine View mit Liste. Damit die View beim Start der Anwendung angezeigt wird, müssen wir sie noch in ApplicationWindow.js registrieren. 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 Affiliate Search API [2], das die benötigten Daten als JSON bereitstellt. Um Daten über HTTP zu laden, können wir die Klasse Titanium.Network.HTTPClient benutzen [3], die ein asynchrones Senden von HTTP Requests erlaubt. Um die asynchron eintreffenden Daten zu verarbeiten, wird der HTTPClient mit zwei Callbacks konfiguriert: onload(event) dient der Verarbeitung der Daten im Erfolgsfall, onerror(event) wird im Fehlerfall aufgerufen. Im Erfolgsfall können die empfangenen Daten im Attribut responseText des HTTPClients abgefragt werden, im Fehlerfall enthält der Parameter event ein Attribut error. Wie ein einfacher HTTP Request 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 Methoden loadData(searchTerm), getSearchUrl(searchTerm) und getDataArray(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, werden 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 getDataArray, 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 Tabellenzelle erzeugt. Abschließend werden diese Tabellenzellen per table.data = data in die Liste übertragen. Die Erzeugung der Zellen selbst ist recht einfach: Mittels Ti.UI.createTableViewCell erzeugen wir eine Listing 2 function SearchView() { var self = Ti.UI.createView({ backgroundColor: 'white' }); var table = Ti.UI.createTableView(); self.add(table); return self; } Abb. 5: Projektstruktur Mobile Technology 1 | 2013 Abb. 6: Master/Detail-App im iOSSimulator module.exports = SearchView; www.mobile360.de
  • Titanium | Mobile Web neue Zelle und setzen die Eigenschaften title und leftImage. 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 Heart So weit, so gut. Nun müssen wir noch die Suche einbinden. Dazu benötigen wir ein Suchfeld, das mit der Tabelle gekoppelt ist. Titanium stellt dazu die Klasse 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; 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(); www.mobile360.de 089 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 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"; } 1 | 2013 Mobile Technology
  • 090 Mobile Web | Titanium dazu, diejenigen Zeilen zu filtern, 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 anwenden. Zunächst setzen wir das Attribut filterAttribute 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 veranschaulicht dies. Natürlich dürfen wir nicht vergessen, den DummyAufruf der Suche nach Madonna zu entfernen – ansonsten erscheint beim Start der Anwendung immer erst mal die Liste ihrer Alben. Abb. 7: Ergebnisliste unter iOS Abb. 8: Ergebnisliste unter Android Listener hinzu, der die Methode loadData aufruft (Listing 6). Besondere Erwähnung verdient in diesem Zusammenhang das Attribut filterAttribute der Tabelle. Es dient Listing 6 var 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, filterAttribute: '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; } Mobile Technology 1 | 2013 Navigation zwischen Screens Wie in Abbildung 4 zu sehen ist, soll beim Tippen auf eine Zelle eine Drill-down-Navigation zu dem entsprechenden 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 clickEvent aufgetreten ist • Event Handler für das Abfangen des albumSelectedEvents 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 AlbumWithTracksView zu finden. Der Event Handler für das Reagieren auf einen Tipp auf eine Tabellenzelle sieht wie folgt aus: table.addEventListener('click', function(event) { self.fireEvent('albumSelected', { data: event.rowData }); }); Das Event enthält die Daten der ausgewählten Zelle (event.rowData), damit der Empfänger des Events auswerten kann, auf welches Album der Benutzer geklickt hat – zu diesem Zweck müssen die Tabellenzellen zusätzlich ein Attribut für die collectionId erhalten. Dieses Event wird nun auf Applikationsebene aufgefangen, um die Navigation zu steuern. Der ApplikationsController sendet das Event einfach an den nächsten in der Navigationshierarchie befindlichen Screen weiter und sorgt dafür, dass dieser Screen dargestellt wird. Unter iOS wird dazu die View in der NavigationGroup geöffnet: www.mobile360.de
  • Titanium | Mobile Web 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 Play Um 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 Navigation 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 Audioplayers, den Titanium unter dem Namen Ti.Media.AudioPlayer zur Verfügung stellt. Über die Eigenschaft url kann eine Referenz auf einen MP3-Stream oder eine Audiodatei ü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 eines neuen Songs den Player zuerst kurz anzuhalten. Der gesamte Ablauf ist in Listing 9 noch einmal zusammengefasst. Barcodes scannen Als zusätzliche Funktionalität soll nun noch anhand des Barcodes einer vorhandenen CD der Künstler ermittelt 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. Appcelerator stellt unter [5] einen Marktplatz für Komponenten zur Verfügung, der über eine große Auswahl an Kompo- www.mobile360.de 091 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 Barcodescanner der Firma Scandit [6], da sie über eine Communityvariante 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(); } 1 | 2013 Mobile Technology
  • 092 Mobile Web | Titanium nach Registrierung unter [7] erhält. Die Erkennung des Barcodes wird von einer kleinen nativen Bibliothek übernommen, die für Android und iOS separat implementiert wurde und im Modul im Verzeichnis für das jeweilige Betriebssystem zu finden ist. Die Installation der Komponente in unsere App erfordert den Download der Komponente vom Appcelerator Marketplace. Nach einem Klick auf den Download-Button auf der Komponentenseite wird die Komponente zunä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 Listing 10 var 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; } Mobile Technology 1 | 2013 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 Modul com.mirasense.scanditsdk auflisten (Abb.  9). Per Doppelklick auf den Eintrag wird das Modul in die Modulliste des Projekts übernommen und steht ab sofort zur Verfügung. Die Verwendung des Barcodescanners in einer eigenen View ist relativ einfach. Zunächst muss das Modul 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 Benutzerinteraktion 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. Diesen Code können wir benutzen, um über einen geeigneten Web Service die Metadaten des gescannten Albums zu beziehen und den Künstler auszuwerten. Zwar unterstützt das iTunes Affiliate Search API angeblich die Suche nach EAN- und UPC-Codes, doch leider funktionierte dies für den Großteil der probehalber gescannten CDs nicht. Glücklicherweise herrscht kein Mangel an alternativen Web Services. Die von uns benötigten 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 view var 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; www.mobile360.de
  • Titanium | Mobile Web 093 =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 passender 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 echtes 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 Applikation zu signieren und über iTunes auf das Gerät zu deployen. Grundvoraussetzung ist natürlich eine Apple-iOS-Developer-Program-Mitgliedschaft sowie ein Provisioning Profile, das Sie über das iOS Provisioning Portal [8] anlegen müssen. Hübschere Zellen Funktional ist unsere App somit vollständig, allerdings lässt die Darstellung der Tabellenzeilen noch etwas zu wünschen übrig. Unter Android werden bereits die Albumcover 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 Moment kommen, an dem die Standard-Layouts für Tabellenzellen nicht mehr ausreichen und man eigene Layouts implementieren muss. Die Erstellung von maßgeschneiderten Tabellenzellen erfolgt durch eine geschickte Kombination von TableViews, TableViewRows, Views, Images und Labels. Titanium verfügt über ein LayoutSystem, das die Erstellung von relativen Layouts erleichtert 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 werden. Um dem Benutzer zu verdeutlichen, dass sich hinter jeder Zelle noch mehr Daten verbergen, soll darüber hinaus ein Disclosure Indicator angezeigt werden (Abb. 10). Die Tabellenzelle selbst erzeugen wir wie gewohnt mit Ti.UI.createTableViewRow. Indem wir das At- www.mobile360.de Abb. 9: Modul zum Projekt hinzufügen tribut hasChild auf true setzen, wird automatisch ein Disclosure Indicator angezeigt. Einen Platzhalter für das Albumcover erzeuAbb. 10: Design einer gen wir mit Ti.UI.createImageView – auch erweiterten Tabellenhier können wir wie schon bekannt einfach zelle den URL zu dem gewünschten Bild angeben und Titanium kümmert sich um den asynchronen Download der Bilddaten. Die beiden Labels fügen wir nicht direkt auf der Tabellenzelle ein, sondern erzeugen zunächst mittels Ti.UI.createView eine ContainerView, 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ändigen Code für das Erstellen der Zelle finden 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}); }); }; 1 | 2013 Mobile Technology
  • 094 Mobile Web | Titanium 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 Implementierung von Apps mittels Titanium flott von der Hand geht. Viele Dinge, die man bei nativer Programmierung unter Android oder iOS aufwändig selbst implementieren müsste, bekommt man bei Titanium oft geschenkt (z. B. einfacher asynchroner Zugriff auf Web Services). Die Implementierung von Oberflächen erfolgt programmatisch über die von Titanium bereitgestellte Abstraktionsschicht, 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 Abstraktionsschicht 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 plattformspezifischen Konzepte kommt man bei der Entwicklung von Titanium-Apps also nicht aus. Auch bei den Navigationskonzepten sind bei den beiden unterstützten Plattformen so deutliche Unterschiede zu verzeichnen, dass wir in unserer 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 asynchronen 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 anderen Herstellern angebotenen Komponenten gut gelöst und funktioniert reibungslos. Da leider noch nicht alle APIs von Titanium selbst gekapselt sind (z. B. fehlt unter 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 erzielenden Roundtrip-Zeiten deutlich langsamer als bei nativer Entwicklung auf der jeweiligen Plattform. Am zügigsten ist noch das Deployment auf den iOS-Simulator – 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 erzeugt und dann mittels iTunes auf das Device synchro- Mobile Technology 1 | 2013 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 herzustellen. 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 flott zu Werke geht und somit die zu debuggende Applikation auch schnell gestartet ist, dauert alleine das Starten der Applikation auf dem Android-Emulator eine gefühlte Ewigkeit (vom Start des Emulators ganz zu schweigen, aber dies ist ein Problem, das man auch bei nativer Programmierung unter Android hat). Alles in allem scheinen die Vorteile sich mit den Nachteilen 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 allerdings überlegen, ob er nicht noch eine weitere native Plattform erlernt. Peter Friese arbeitet als Software Architect und Principal Consultant 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 www.mobile360.de