Phonegap: Mobile mal anders

Mit NativeScript für Android-Devices entwickeln
Keine Kommentare

PhoneGap ermöglichte Java-Script-Entwicklern das Erzeugen von Applikationen, die sich im Store vertreiben ließen. Zur damaligen Zeit war dies insofern eine Revolution, als die Apps endlich das „Gefängnis“ des Browsers verlassen konnten. Doch war damit das Ende der Fahnenstange erreicht?

Denn in der Praxis zeigte PhoneGap jedoch schon bald seine Zähne. Das lag vor allem daran, dass die Programme in einer eingebetteten Web View ausgeführt wurden: Ob der fehlenden Möglichkeit zum Zugriff auf den nativen GUI-Stack sahen die Programme trotz diverser Steuerelementbibliotheken „eigen“ aus. Zudem war der Zugriff auf APIs nur über Plug-ins möglich: Wer einmal ein Plug-in entwickelt hat, ist von diesbezüglichen Ambitionen geheilt.

Die Verfügbarkeit von quelloffenen und hochleistungsfähigen JavaScript-Interpretern wie Googles V8 verwandelte die Websprache zu einer seriösen Runtime. Das bei Telerik als quelloffenes Projekt verwaltete NativeScript nutzt V8 zur Realisierung einer Laufzeitumgebung, die ob eines Maklers direkt mit nativen Elementen arbeitet. Die Vorteile dieser Struktur lassen sich anhand des in Abbildung 1 gezeigten Architekturdiagramms zusammenfassen.

Abb. 1: Alles Laufzeit oder was?

Abb. 1: Alles Laufzeit oder was?

Die V8-Engine wurde von Telerik um eine Möglichkeit zur Reflektion erweitert – es ist problemlos möglich, nach folgendem Schema auf native APIs zuzugreifen:

var time = new android.text.format.Time();
time.set( 1, 0, 2015 );
console.log( time.format( "%D" ) );

Da dies keine Vorteile gegenüber der Nutzung von Java und Objective-C bietet, gibt es mit den Modulen von Telerik bereitgestellte Bibliotheken, die häufige Aufgaben übernehmen. Da Telerik diese für Android und iOS gleichermaßen wartet, eignet sich NativeScript bei Beschränkung auf Bibliotheksfunktionen als Cross-Plattform-Runtime.

Als besondere Stärke gilt die Integration in den nativen GUI-Stack. NativeScript arbeitet mit im XML-Format vorliegenden Benutzerschnittstellendefinitionen, die von der Runtime in native Steuerelemente umgewandelt werden. Die aus diversen Tags zusammengebauten GUIs treten zur Laufzeit wie eine in den nativen Entwicklungswerkzeugen erstellte Benutzerschnittstelle auf: Die bei Emulation mit CSS auftretenden Unterschiede zu „normalen“ Applikationen sind bei NativeScript nicht zu befürchten.

Eine Frage des Werkzeugs

Das von Telerik als quelloffen entwickelte Framework wird nicht aus reiner Eigenliebe bereitgestellt: Die kostenlose Version zwingt Entwickler zum Arbeiten mit der PhoneGap-CLI. Zahlungswillige Kunden können stattdessen auf einen Webdienst, eine lokal lauffähige IDE oder ein Plug-in für Visual Studio zurückgreifen. Wir wollen im folgenden Artikel den Webdienst Telerik Platform verwenden, der über den Button Start Now dreißig Tage kostenlos zur Verfügung steht.

Nach der Anmeldung setzt die IDE ihre Nutzer in einem Tutorial ab, das für uns von geringer Relevanz ist. Klicken Sie auf das Logo auf der linken Seite des Bildschirms, um in die Startseite Ihres Kontos zu wechseln. Wählen Sie die Option Start from a blank app, und vergeben Sie als Namen SUSSample1. Telerik Platform orientiert sich an Visual Studio – nach der Erstellung der „Solution“ folgt ein Klick auf den Button Create AppBuilder Native Project, der die Solution um eine GUI-Applikation ergänzt. Der in Abbildung 2 gezeigte Assistent erlaubt die Auswahl von Projektvorlagen, in denen die von Haus aus voreingestellten Optionen für JavaScript-Entwickler ideal geeignet sind. TypeScript-Nutzer sollten stattdessen eine der TS-Vorlagen wählen.

Abb. 2: Bitte wählen Sie Ihre Vorlage aus

Abb. 2: Bitte wählen Sie Ihre Vorlage aus

Nach der Eingabe eines Namens erzeugt Telerik Platform die in Abbildung 3 gezeigte Projektstruktur, die uns erste Einblicke in den Aufbau der Solution erlaubt.

Abb. 3: Das Projektskelett ist fertig

Abb. 3: Das Projektskelett ist fertig

Der Inhalt von app.js sollte Node.js-erfahrenen Entwicklern bekannt vorkommen: Wir beschaffen eine Instanz der Application-Klasse durch Aufruf von require(), weisen ihr Attribute zu und rufen danach Start auf:

var application = require("application");
application.mainModule = "main-page";
application.cssFile = "./app.css";
application.start();

Im Moment ist das XML-File des Hauptformulars leer: Es besteht nur aus der Deklaration eines Page-Tags, das für uns nicht weiter relevant ist. Klicken Sie main-page.xml doppelt an, um seinen Code im Editor zu öffnen. Ersetzen Sie den dort vorhandenen Inhalt durch den in Listing 1 gezeigten Code.

<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="mainPageLoaded" >
  <StackLayout>
    <Label text="Name:" />
    <TextField id="name" width="200"/>

    <Label text="Password:" />
    <TextField id="password" width="200"/>

    <Button id="signUpButton" text="Sign Up" width="100"/>
  </StackLayout>
</Page>

Die in der Einleitung erwähnte Transformation in native Steuerelemente erfolgt durch Tags. Android-Entwickler werden einige der hier erwähnten Widgets instinktiv wiedererkennen. In der im Abschnitt „Mehr lernen“ referenzierten Dokumentation findet sich eine Tabelle mit einer vollständigen Auflistung aller vorhandenen Widget-Klassen.

Für uns ist im nächsten Schritt die Implementierung der Methode mainPageLoaded erforderlich, die nach dem erfolgreichen Aufbau der Benutzerschnittstelle aufgerufen wird. Sie ist für die Parametrierung der einzelnen Steuerelemente zuständig. Neben dem Einschreiben von Default- oder im Remanent-Speicher gespeicherten Werten erfolgt hier auch die Zuweisung von Event Handlern und ähnlichen Lustigkeiten.

Klicken Sie die Datei main-page.js doppelt an, um sie im Editor zu öffnen. Passen Sie den dort befindlichen Code danach gemäß im Listing 2 gezeigten Schema an.

var view = require("ui/core/view");

function myMainPageFunc(args)
{
  var page = args.object;
  var nameField = view.getViewById(page, "name");
  var passField = view.getViewById(page, "password");
  var signUpButton = view.getViewById(page, "signUpButton");
  signUpButton.on("tap", function () {
    if(nameField.Text==="Tester" && passField.Text==="Test")
    {
      alert("Hallo Welt");
    }
    else
    {
      alert("Login-Fehler");
    }
  });
}
exports.mainPageLoaded=myMainPageFunc;

Auch diese Datei beginnt – Node.js-Entwickler lachen mittlerweile freudig – mit einem Aufruf von require(). Diesmal beschaffen wir einen Verweis auf das View-Objekt, das als Metaklasse für alle am Bildschirm angezeigten Steuerelemente dient.

Im nächsten Schritt folgt die Deklaration der Funktion myMainPageFunc. Sie beschafft Verweise auf die in der XML-Datei deklarierten Steuerelemente und schreibt dem Button einen Handler für das als „Tap“ bezeichnete Klickevent ein. Sein Code beschränkt sich im Moment auf die Anzeige von Alerts – wir werden später die Anzeige eines weiteren Formulars implementieren.

Die mit Exports beginnende Zeile ist von besonderer Bedeutung. Die Zuweisung von Funktionen an im XML deklarierten Event Handlern erfolgt immer über das Exports-Objekt; die Engine wäre nicht in der Lage, eine beliebige Funktion Namens mainPageLoaded zu finden und als Event Handler auszuführen.

Zur Vermeidung von „Pollution“ dieses Namespace ist es ratsam, nur die wichtigsten Funktionen (Formular wird geladen) als Export zu deklarieren, und alle anderen Event Handler – wie im Snippet gezeigt – zur Laufzeit einzuschreiben.

Lasset uns Testen

Eine im Web laufende Entwicklungsumgebung kann nur mit vergleichsweise hohem Aufwand – codebender lässt grüßen – Kontakt zu an der Workstation angeschlossener Hardware aufnehmen. Telerik umgeht dieses Problem durch Einführung einer Clientapplikation. Es handelt sich dabei um eine generische Version der Runtime, die von Telerik Platform aus mit einem Kompilat versorgt werden kann, das daraufhin am Telefon zur Ausführung gelangt.

Der erste Schritt besteht darin, alle geöffneten Dateien durch Anklicken des Save-Buttons in der IDE zu sichern. Klicken Sie daraufhin auf Run | Build und wählen sie im Dialog Set Target-Platform Android → Native-Script-Companion-App aus. Klicken Sie im nächsten Schritt auf Next – das Produkt generiert daraufhin einen QR-Code, der mit einem dem Codescanner der Companion-App eingelesen werden kann.

Falls Sie diese noch nicht auf ihrem Telefon installiert haben, lässt sich dies durch Anklicken des Install-Buttons beheben. Die IDE bietet daraufhin einen Link zur App an, die auf Ihr Telefon wandern muss.

Abb. 4: Applikationen werden über die Notification geladen

Abb. 4: Applikationen werden über die Notification geladen

Nach dem erstmaligen Öffnen blendet die App die in Abbildung 4 gezeigte Notification ein; ist sie nicht mehr am Bildschirm, so müssen Sie die Companion-App aus dem Launcher heraus starten.

Klicken Sie auf den Scan-Button, um den QR-Scanner zu aktivieren – er erlaubt das Einscannen des von Telerik Platform erzeugten Codes. Sync holt die aktuellste Version des gerade verlinkten Projekts vom Server – achten Sie lediglich darauf, die Dateien im Backend vor dem Anklicken des Buttons zu speichern.

Das Programm selbst lässt sich durch Anklicken der Native-Script-App starten – Neudownloads und Resynchronisationen müssen über die immer eingeblendete Notification erfolgen. Unser Programm präsentiert sich im Moment wie in den Abbildungen 5 und 6 gezeigt.

Abb. 5: Das Formular sieht im Landscape- …

Abb. 5: Das Formular sieht im Landscape- …

Abb. 6: … und im Portrait-Mode gleichermaßen professionell aus

Abb. 6: … und im Portrait-Mode gleichermaßen professionell aus

Wenn die Companion-App nach dem Laden des Projekts nur einen weißen Bildschirm anzeigt und alle fünf bis fünfzehn Sekunden neu startet, so liegt ein Mismatch zwischen der im Projekt und in der Companion-Applikation implementierten TypeScript-Version vor.

Zum Zeitpunkt der Drucklegung implementierte die Android-App nur die Version 1.0.1 und kann mit der in der IDE von Haus aus voreingestellten Version nichts anfangen. Erfreulicherweise lässt sich dies im Properties-Dialog der Solution anpassen. Denken Sie nur daran, Ihr Projekt per Run neu zu kompilieren und den neu generierten QR-Code abermals einzuscannen.

Seite, wechsle dich

Nach diesen einführenden Überlegungen ist es an der Zeit, eine neue Seite anzulegen. Klicken sie dazu auf add new file, und erzeugen Sie eine neue Datei Namens LoggedInPage.xml anhand der Vorlage „Blank Page“.

Falls das Formular nicht im Unterordner /App/ zum liegen kommt, können Sie es per Drag-and-Drop an seinen neuen Aufenthaltsort verschieben. Die Erzeugung der Codedatei erfolgt ebenfalls durch Klick auf Add new file; als Vorlage wird nun jedoch JavaScript gewählt.

Öffnen Sie im nächsten Schritt die Datei LoggedInOage.xml und passen Sie ihren Korpus gemäß des Schemas in Listing 3 an.

<Page xmlns="http://www.nativescript.org/tns.xsd"loaded="liPageLoaded">
  <StackLayout>
    <ListView items="{{ myItems }}">
      …
    </ListView>
    <Button id="addBtn" text="Add" width="100"/>
  </StackLayout>
</Page>

Auf den ersten Blick unterscheidet sich diese Layoutdatei nur wenig von ihrem Vorgänger. Statt der Nutzung von Buttons und Labels füllen wir das Stack-Layout dieses Mal mit einer Instanz der Listview-Klasse.

Deren Deklaration ist insofern interessant, als die Items-Eigenschaft einen in zwei geschwungene Klammern gesetzten String enthält. Es handelt sich dabei um eine Binding-Expression – Kenner von Knockout ahnen sofort, dass diese von der Datenbindungs-Engine zur Laufzeit mit den im Modell befindlichen Informationen bevölkert wird.

Die Darstellung der einzelnen Datenzeilen erfolgt in ListViews durch eine als „itemTemplate“ bezeichnete Struktur, die als Kind der ListView anzulegen ist. Es handelt sich dabei um eine Vorlage, die einmal pro Element errichtet und mit den im Datenmodell befindlichen Informationen gefüllt wird.

Die Anordnung am Bildschirm erfolgt in unserem Fall durch ein GridLayout. Es handelt sich dabei um eine Tabelle für Steuerelemente, deren Zeilen und Spalten im Rahmen der Deklaration dimensioniert werden müssen. In unserem Fall ist die erste Spalte immer achtzig Pixel breit, während die zweite den Bildschirm ausfüllt. Die Reihen sind jeweils vierzig Pixel hoch (Listing 4).

<ListView.itemTemplate>
  <GridLayout columns="80, *" rows="40,40">
    <Label text="Feld A" col="0" row="0"/>
     <TextView text="{{ FeldA }}" col="1" row="0"/>
     <Label text="Feld B" col="0" row="1"/>
     <TextView text="{{ FeldB }}" col="1" row="1"/>
  </GridLayout>
</ListView.itemTemplate>

Achten Sie darauf, dass die NativeScript-Engine – zumindest in der vorliegenden Version – auf Imbalancen in xml-Dateien sehr aggressiv reagiert. Als Beispiel dafür dient ein winziger Fehler in Listing 5.

<ListView.itemTemplate>
  <GridLayout columns="80, *" rows="40,40">
    <Label text="Feld A" col="0" row="0"/>
    <TextView text="{{ FeldA }}" col="1" row="0"/>
    <Label text="Feld B" col="0" row="1"/>
    <TextView text="{{ FeldB }}" col="1" row="1"/>
  </StackLayout>
</ListView.itemTemplate>

Das Nichtzusammenpassen des öffnenden und des schließenden Tags sorgt dafür, dass die Companion-App beim Starten des Kompilats ohne jegliche Fehlermeldung die Arbeit einstellt. Die in früheren Versionen der Companion-App eingeblendete Syntaxwarnung war während unseres Tests mit der aktuellsten Version des Produkts nicht mehr sichtbar.

Im Code-Behind muss Data Binding von Hand initialisiert werden. Wir erledigen dies in myLogPageFunc – der Korpus der Methode sieht so aus:

function myLogPageFunc(args)
{
  var page = args.object;
  page.bindingContext = { myItems: [{ FeldA: "NameAA", FeldB: "NameAB" }, { FeldA: "NameBA", FeldB: "NameBB" }, { FeldA: "NameCA", FeldB: "NameCB" }] };
}
exports.liPageLoaded=myLogPageFunc;

An dieser Stelle ist nur die Nutzung der bindingContext-Eigenschaft der Page interessant. Sie nimmt alle Objekte auf, die für die Datenbindungs-Engine ansprechbar sein sollten. Ist etwas nicht im bindingContext enthalten, so ist es für die Engine zur Laufzeit nicht erreichbar.

Die Aktivierung des Formulars erfolgt in der MainPage; der Event Handler des „Sign-Up“-Buttons wird wie in Listing 6 gezeigt angepasst.

signUpButton.on("tap", function () {
  if(nameField.Text==="Tester" && passField.Text==="Test")
  {
    alert("Login erfolgreich");
  }
  else
  {
    var frames = require("ui/frame");
    frames.topmost().navigate("LoggedInPage");
  }

Puristen bemängeln an dieser Stelle, dass der Logged-in-Dialog beim erfolglosen Validieren aktiviert wird. Dieses auf den ersten Blick wie ein klassischer Tippfehler aussehende Konstrukt ist beabsichtigt: Sie können den Dialog beim Debugging direkt nach dem Start durch Antippen von Sign-in aktivieren, ohne jedes Mal Benutzername und Passwort eingeben zu müssen.

Damit sind wir fürs Erste fertig. Abbildung 7 zeigt, wie NativeScript die im Array enthaltenen Informationen in die Liste schreibt.

Abb. 7: Die Liste wird automatisch gefüllt

Abb. 7: Die Liste wird automatisch gefüllt

Datenbindung macht Freude

Java-Script macht Entwicklern von Data-Binding-Engines die Arbeit insofern schwer, als dass Variablen und Arrays keine Möglichkeit zur Anmeldung von Änderungen mitbringen. Das bedeutet, dass Drittobjekte nicht darüber informiert werden, wenn sich in den von ihnen beobachteten Elementen etwas ändert.

Zur Umgehung dieses Problems hat sich die Nutzung von Hilfsklassen etabliert, die in Literatur und Praxis als „Observables“ bezeichnet werden. Es handelt sich dabei um Speicherklassen, die bei Veränderungen ihres Datenbestands – also dem Hinzukommen oder Verschwinden von Elementen und/oder der Änderung von Werten – Events abfeuert.

Als ersten Akt wollen wir die in der Liste angezeigten Informationen in ein ObservableArray packen. Die neue Version von myLogPageFunc ist in Listing 7 zu sehen.

var observableArray = require("data/observable-array");

function myLogPageFunc(args)
{
  var page = args.object;
  var obsA = new observableArray.ObservableArray([{ FeldA: "NameAA", FeldB: "NameAB" }, { FeldA: "NameBA", FeldB: "NameBB" }, { FeldA: "NameCA", FeldB: "NameCB" }]);
  page.bindingContext = { myItems:  obsA};
  var addButton = view.getViewById(page, "addBtn");
  addButton.on("tap", function () {
    obsA.push({ FeldA: "NameXX", FeldB: "NameXX" });
  });
}

Wenn Sie das Programm in der vorliegenden Form ausführen und auf den Add-Knopf klicken, so erscheint eine zusätzliche Spalte in der Tabelle. Sie können den Button ruhig mehrfach anklicken, um die Tabelle so immer weiter zu bevölkern.

Leider sind wir damit noch nicht am Ziel. Änderungen am Datenbestand der einzelnen Elemente führen im Moment nicht zu Änderungen im Datenmodell; die im ObservableArray eingeschriebenen Objekte sind nach wie vor normale JavaScript-Variablen.

Dieses Problem lässt sich durch Einschreiben von Observables lösen, die für die einzelnen Attribute zuständig sind. Die Implementierung der Methode sieht dann so aus:

function myLogPageFunc(args)
{
  var page = args.object;
  var obsA = new observableArray.ObservableArray([
    new observableModule.Observable({ FeldA: "NameAA", FeldB: "NameAB" }), 
    new observableModule.Observable({ FeldA: "NameBA", FeldB: "NameBB" }), 
    new observableModule.Observable({ FeldA: "NameCA", FeldB: "NameCB" })]);
  page.bindingContext = { myItems:  obsA};
  ...

Zum Test des Features realisieren wir einen „Bicker“-Knopf, der die Werte eines bestimmten Felds aller Elemente des ObservableArrays durch XXX ersetzt. Sein Event Handler sieht aus wie in Listing 8 zu sehen.

function myLogPageFunc(args)
{
  ...
  var i;
  var bickerButton = view.getViewById(page, "bickerBtn");
  bickerButton.on("tap", function () {
    for(i=0;i<obsA.length;i++)
    {
      var attr=obsA.getItem(i);
      attr.set("FeldA", "XXX");
    }
  });
}

An dieser Stelle ist die Nutzung der set-Methode interessant. NativeScript Observables sind vollwertige Objekte, die mehrere Attribute in sich zusammenfassen. Dieses für Knockout-erfahrene Entwickler unübliche Verhalten zeigt sich insofern, als beim Interagieren mit Eigenschaften der als String vorliegende Parametername erforderlich ist.

Damit sind wir an dieser Stelle fertig. Schicken Sie das Programm abermals auf Ihr Handy, um es zu testen – das Anklicken des Bigger-Buttons sorgt dafür, dass alle Instanzen von Feld B automatisch durch den String „XXX“ ersetzt werden (Abb. 8).

Abb. 8: Die XXX machen sich in unserem Formular breit

Abb. 8: Die XXX machen sich in unserem Formular breit

Einstellungen remanent speichern

Als nächste Aufgabe wollen wir uns dem Laden und Speichern der in der Liste verwalteten Informationen zuwenden. Hierzu sei ein Hinweis auf das App-Settings-Modul angebracht: Normalerweise würde man Speichervorgänge von Datenbeständen geringer Mächtigkeit über den ebenda realisierten KV-Speicher realisieren. Wir nutzen das Filesystemmodul hier nur aus didaktischen Gründen.

Als erster Akt müssen wir Code ausführen können, wenn das Formular vom Bildschirm verschwindet. Dies erfolgt durch folgende Änderung im XML-Code – neben dem schon bekannten Handler für „Loaded“ wird nun auch eine Methode für „Unloaded“ implementiert: <Page xmlns=“http://www.nativescript.org/tns.xsd“loaded=“liPageLoaded“ unloaded=“liPageGone“>.

Die Implementierung von myLogGoneFunc muss einen Verweis auf den Dateisystemmanager beschaffen. Dies erfolgt durch ein require() auf den String file-system. Danach werden die im Observable-Array befindlichen Informationen – man beachte das Auslesen aus page.bindingContext – durch klassische manuelle Serialisierung in einen speicherbaren String umgewandelt.

Im nächsten Schritt folgt ein Aufruf von knownfolders.documents. Diese Methode liefert den Pfad des privaten Verzeichnisses der jeweiligen Applikation zurück, der sodann zur Generierung einer neuen Datei dient.

WriteText überschreibt ihren Inhalt sodann mit dem im Rahmen der Deserialisierung entstandenen String. Ein Alert-Dialog informiert den Nutzer über den Erfolg und/oder Misserfolg dieser asynchron ablaufenden Operation (Listing 9).

var fs = require("file-system");

function myLogGoneFunc(args)
{
  var page = args.object;
  var obsA=page.bindingContext.myItems;
  var storString="";
  for(i=0;i<obsA.length;i++)
  {
    var attr=obsA.getItem(i);
    storString=storString + attr.get("FeldA") + "/" + attr.get("FeldB") + "/";
  }

  var documents = fs.knownFolders.documents();
  var path = fs.path.join(documents.path, "MyFile.stt");
  var file = fs.File.fromPath(path);
  file.writeText(storString).then(function () {
    alert("Speicherung erfolgreich!")
  }, function (error) {
    alert("Speicherung gescheitert!")
  });

}
exports.liPageGone=myLogGoneFunc;

Asynchron ablaufende Methoden zeichnen sich in NativeScript dadurch aus, dass sie ein als „Promise“ bezeichnetes Objekt zurückliefern. Dieses aus ECMAScript 6 bekannte Element besteht aus zwei Funktions-Pointern, die bei der erfolgreichen oder fehlgeschlagenen Ausführung der Operation aufgerufen werden. Weitere Informationen zu diesem Feature gibt es hier – achten Sie darauf, dass Promises in Browsern noch nicht flächendeckend zur Verfügung stehen.

Für uns geht es an dieser Stelle mit dem Laden der gespeicherten Informationen weiter. Dieser Prozess geschieht in der für die allgemeine Initialisierung zuständigen Methode myLogPageFunc, die nun mit der Erzeugung der Variablen für das ObservableArray beginnt.

Diese darf keinesfalls in einer der for-Schleifen entstehen, da die Position der Deklaration für die Errichtung der Closure des hier nicht nochmals abgedruckten Knopf-Event-Handlers von Bedeutung ist (Listing 10).

var fs = require("file-system");

function myLogPageFunc(args)
{
  var page = args.object;

  var obsA;
  var documents = fs.knownFolders.documents();
  var path = fs.path.join(documents.path, "MyFile.stt");
  if(fs.File.exists(path))
  {
    var file = fs.File.fromPath(path);
    file.readText().then(function (content) {
      var workArr=content.split("/");
      obsA = new observableArray.ObservableArray(0);
      for(i=0;i<workArr.length-1;i=i+2)
      {
        obsA.push(new observableModule.Observable({ FeldA: workArr[i], FeldB: workArr[i+1] })); 
      }
      page.bindingContext = { myItems:  obsA};
    }, function (error) {  });

Ist die Einstellungsdatei vorhanden, so laden wir die in ihr enthaltenen Informationen Schritt für Schritt in das ObservableArray. An dieser Stelle verdient die for-Schleife besondere Erwähnung, denn sie springt in Zweierschritten weiter.

Das Subtrahieren von Eins gegen „Length“ ist notwendig, weil JavaScript am Ende jedes serialisierten Elements ein „/“ anhängt. Bei der Deserialisierung wird das letzte „/“ zu einem leeren Element, das sich beim Laden in Form einer „Phantomzeile“ äußert – ein Problem, das durch die Subtraktion elegant aus der Welt geschafft wird.

Spiele mit Bildern

NativeScript ist für die Verarbeitung von Bitmap-Daten im Moment eher schlecht als recht gerüstet: Es gibt keinen Canvas-Ersatz, der das Ausführen von Zeichenoperationen (Schema: Mache eine Linie von X bis Y) ermöglicht. Diesbezügliche Erweiterungsanträge sind zwar im Bugtracker, bekamen aber aus technischen Gründen bisher nur wenig Aufmerksamkeit.

Trotzdem gibt es mit dem Image-Steuerelement schon jetzt eine Klasse, die im Dateisystem oder im Internet befindliche Bilder herunterladen kann. Die einfachste Variante beschafft ein unter einem URL zur Verfügung stehendes File:

<Image src="https://www.google.com/images/errors/logo_sm_2.png" / >

Besonders interessant ist das Laden von im Ressourcensystem der jeweiligen Plattform vorliegenden Bildern. Dazu muss der in das Source-Tag eingeschriebene URL mit dem Präfix res:// beginnen.

Die Bevölkerung der Ressourcensysteme erfolgt in Telerik Platform über das Verzeichnis /App/App_Resources/. In den Unterordnern Android und iOS finden sich Verzeichnisstrukturen, die mit den jeweiligen Betriebssystemen vertrauten Entwicklern mit Sicherheit bekannt vorkommen dürften. NativeScript kopiert die hier hinterlegten Ressourcen während der Kompilation mit dem nativen Ressourcenpacker: Die Ressourcenauswahl-Engines von Android und iOS funktionieren problemlos weiter.

Zu guter Letzt sei an dieser Stelle noch auf die ImageSource-Klasse hingewiesen, deren Verwendung in folgendem Snippet illustriert ist:

var image = new imageModule.Image();
var imageSource = imageSourceModule.from*;
image.imageSource = imageSource;

Die from*-Methoden erlauben das Beschaffen von Bildinformationen zur Laufzeit; neben Downloads ist auch der Zugriff auf das lokale Dateisystem und das Ressourcensystem zulässig. Pixelinformationen lassen sich dann zur Anzeige in Image-Steuerelemente einschreiben, was das dynamische Anpassen des Aussehens der Applikation erleichtert.

Mehr lernen

Wer mehr über NativeScript erfahren möchte, sollte die bereitstehende offizielle Dokumentation konsultieren. Ihr Aufbau ist insofern verwirrend, als sie nach dem Laden der Seite vor einer Liste von im Tutorialstil aufgebauten Inhalten stehen.

Die Parameterlisten der einzelnen Klassen lassen sich durch den API-Reference-Ordner auf den Bildschirm holen: Klicken Sie ihn einmal links an, um die in Abbildung 9 gezeigte Verzeichnisstruktur aufzuklappen.

Abb. 9: Informationen zu den einzelnen Klassen sind gut versteckt

Abb. 9: Informationen zu den einzelnen Klassen sind gut versteckt

Fazit

Auch wenn die Entwicklung der Windows-Phone-Variante von NativeScript zum Zeitpunkt der Drucklegung noch nicht abgeschlossen ist, zeigt das Produkt schon jetzt immenses Potenzial. Wer sich mit Qt nicht anfreunden kann und das Aussehen der diversen PhoneGap-GUI-Stacks als potthässlich empfindet, findet hier eine attraktive Alternative. Der Preis der Web-IDE mag auf den ersten Blick abschreckend wirken, wird bei populären Applikationen aber auch durch das unaufdringlichst platzierte Banner eingespielt.

Aufmacherbild: Woman using smart phone von Shutterstock / Urheberrecht: Balazs Kovacs Images

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -