Titanium Mobile: Die Erstellung eines Bild-Uploaders
German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)
In diesem Tutorial wird gezeigt, wie Sie einen benutzerdefinierten Fortschrittsbalken erstellen, indem Sie mit Titanium Mobile einen Bild-Hochlader erstellen. Im Demo-Projekt können Sie insbesondere ein Bild aus der Geräte-Fotogalerie auswählen und zur Speicherung auf einen Remote-Server hochladen. Sowohl der Titanium Mobile-Code als auch der serverseitige Code werden erläutert. Jetzt fangen wir an!
Projektvorschau
Das Folgende ist eine Vorschau der plattformübergreifenden Anwendung, die wir in dieser Serie erstellen werden:






Voraussetzungen...
In diesem Tutorial wird davon ausgegangen, dass Sie Titanium Studio ausführen und die neuesten SDK-Updates installiert haben, die zum Zeitpunkt des Schreibens 1.7.5 waren. Da sich dieses Tutorial auf die iOS-Plattform konzentriert, würde der Leser auch davon profitieren, alle Anforderungen für die Entwicklung auf der iOS-Plattform zu haben.
Schritt 1: Die Erstellung eines neuen Projekts
Öffnen Sie Titanium Studio und erstellen Sie ein neues Projekt. Wählen Sie nur die Geräteziele für Android, iPhone und iPad aus. Sie können Ihr neues Projekt beliebig benennen, wir nennen es jedoch "ProgressBar". Geben Sie Ihrem neuen Projekt eine App-ID und eine URL und wählen Sie dann die neueste Titanium SDK-Version aus. Wenn Sie fertig sind, klicken Sie auf "Next" und wählen Sie dann die Vorlage für die Einzelfensteranwendung aus, bevor Sie auf "Finish" klicken.






Wenn die Vorlagenoption "Single Window Application" in Ihrer Titanium-Version nicht verfügbar ist, können Sie stattdessen den an dieses Lernprogramm angehängten Projektquellcode importieren. Laden Sie die Projektdateien herunter und entpacken Sie sie in das Verzeichnis, aus dem Sie arbeiten möchten. Befolgen Sie dann diese Schritte:
- Wählen Sie im Menü Datei die Option "Import", um das Popup-Fenster "Projekt importieren" zu öffnen.
- Wählen Sie aus der Liste der Importquellen "Titanium" und dann "Import Existing Titanium Project".
- Navigieren Sie im Textfeld Verzeichnis zu dem Ordner, der die heruntergeladenen und entpackten Projektdateien enthält.
- Klicken Sie auf die Schaltfläche Fertig stellen. Ein neues Projekt mit dem Namen "ProgressBar" wird erstellt und in Ihrem Projektexplorer-Bereich verfügbar gemacht.
Wenn Sie dies noch nicht getan haben, ist jetzt ein guter Zeitpunkt, um die Projektdateien herunterzuladen und alle Bilder im Verzeichnis "Resources/assets/images" in Ihr eigenes Verzeichnis "Resources/assets/images" unter dem neuen Projekt zu entpacken gerade erstellt. Standardmäßig enthält Titanium auch zwei Symboldateien im Stammverzeichnis Ihres "Resources"-Verzeichnisses mit den Namen KS_nav_ui.png und KS_nav_views.png. Wir benötigen diese ebenfalls nicht. Verschieben Sie beide Dateien in den Papierkorb. Sie können diesen Schritt überspringen, wenn Sie bereits das gesamte Projekt mit der Funktion "Import" importiert haben.
Schritt 2: Die Erstellung der Schnittstelle
Da die Single Window Application-Vorlage der CommonJS-Struktur folgt, unterscheidet sie sich möglicherweise ein wenig von dem, was Sie gewohnt sind. Hier ruft die Datei app.js die Datei ApplicationWindow.js mit 'require' ab, wodurch unser Standardfensterobjekt für die Anwendung instanziiert und zurückgegeben wird. Die ApplicationWindow-Datei instanziiert eine neue Ansicht namens FirstView, die in der Datei FirstView.js im Ordner 'Resources/ui' codiert ist. In diesem FirstView-Ansichtsobjekt werden wir fast das gesamte Tutorial codieren. Löschen Sie vorgenerierten Code im Konstruktor exports.FirstView = function() und ersetzen Sie ihn durch den folgenden:
//FirstView Component Constructor exports.FirstView = function() { // Let's hide the status bar on the iphone/ipad for neatness if(Ti.Platform.osname == 'iphone' || Ti.Platform.osname == 'ipad'){ Titanium.UI.iPhone.statusBarHidden = true; } // Create object instance, a parasitic subclass of Observable var self = Ti.UI.createView({ backgroundColor: '#232323' }); // The view below is the background of the slider var progressBackgroundView = Ti.UI.createView({ width: 300, height: 27, left: ((Ti.Platform.displayCaps.platformWidth - 300) / 2), top: (Ti.Platform.displayCaps.platformHeight / 2), visible: false, backgroundImage: 'assets/images/track-complete.png' }); self.add(progressBackgroundView); //the slider will show a graphical representation of the upload progress //backgroundImage will reduce flicker as it doesn't redraw every width change like 'image' will var progressView = Ti.UI.createImageView({ width: 0, height: 25, left: 1, top: 1, backgroundImage: 'assets/images/bar.jpg', borderRadius: 3 }); progressBackgroundView.add(progressView); //this label will show the upload progress as a percentage (i.e. 25%) var lblSending = Ti.UI.createLabel({ width: 'auto', right: ((Ti.Platform.displayCaps.platformWidth - 300) / 2), top: ((Ti.Platform.displayCaps.platformHeight / 2) + 30), text: '', height: 20, font: {fontFamily: 'Arial', fontSize: 14, fontWeight: 'bold'}, color: '#fff', textAlign: 'right', visible: false }); self.add(lblSending); //this button will appear initially and allow the //user to choose a photo from their gallery var btnChoosePhoto = Ti.UI.createButton({ width: 220, height: 35, title: 'Select photo for upload.', font: {fontFamily: 'Arial'}, color: '#000000', top: (Ti.Platform.displayCaps.platformHeight / 2), visible: true }); self.add(btnChoosePhoto); return self; };
Oben haben wir die Grundstruktur unseres Fortschrittsbalkens und eine Schaltfläche erstellt, mit der der Benutzer die Fotogalerie auf seinem Gerät öffnen und ein Bild auswählen kann, das er hochladen möchte. Führen Sie die Anwendung jetzt im Simulator aus, und in der Mitte des Bildschirms sollte eine einzelne Schaltfläche angezeigt werden, die ungefähr so aussieht:



Schritt 3: Auswählen eines Bildes aus der Galerie
Nachdem wir unsere grundlegende Benutzeroberfläche eingerichtet haben, fügen wir btnChoosePhoto einen Ereignis-Listener und -Handler hinzu, mit dem unser Benutzer ein Foto aus seiner Galerie auswählen kann. Geben Sie den folgenden Code direkt vor der Zeile 'self.add (btnChoosePhoto);' ein.
btnChoosePhoto.addEventListener('click', function(e){ Titanium.Media.openPhotoGallery({ success:function(event) { Ti.API.debug('Our type was: '+event.mediaType); if(event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO) { UploadPhotoToServer(event.media); } }, cancel:function() { }, error:function(err) { Ti.API.error(err); }, mediaTypes:[Ti.Media.MEDIA_TYPE_PHOTO] }); });
In diesem "Klick" -Ereignishandler öffnen wir die Galerie und überprüfen im Erfolgsabschnitt des Codes, ob der ausgewählte Medientyp ein Foto war, indem wir das Objekt mit dem konstanten Wert Ti.Media.MEDIA_TYPE_PHOTO vergleichen. Wenn diese Prüfung erfolgreich ist, übergeben wir als nächstes unser event.media-Objekt an eine neue Funktion namens UploadPhotoToServer. Hier führen wir sowohl den Datei-Upload durch als auch dem Benutzer unseren Fortschrittsbalken beim Hochladen.
Schritt 4: Die Erstellung des PHP-Servercodes zum Akzeptieren und Speichern unseres hochgeladenen Fotos
Wir werden unseren Servercode erstellen, um unser Foto aus der Ferne zu speichern und seine URL mit PHP zurückzugeben. PHP ist eine bekannte Web-Sprache, die von vielen gängigen Webhosting-Diensten unterstützt wird. Erstellen Sie eine neue Datei mit dem Namen upload.php und geben Sie den folgenden Code in diese Datei ein:
<?php //this function returns a random 5-char filename with the jpg extension function randomFileName() { $length = 5; $characters = 'abcdefghijklmnopqrstuvwxyz'; $string = ''; for ($p = 0; $p < $length; $p++) { $string .= $characters[mt_rand(0, strlen($characters))]; } return $string . '.jpg'; } //create the random filename string and uploads target variables $randomString = randomFileName(); $target = 'uploads/'; $target = $target . $randomString; if(move_uploaded_file($_FILES['media']['tmp_name'], $target)) { //output the location to our image echo 'https://mobiletuts.example.com/progressbar/uploads/' . $randomString; } else { echo "false"; } ?>
Dieses grundlegende PHP-Skript nimmt einfach eine POSTED-Datei namens 'media' auf und speichert sie dann mit einem zufälligen fünfstelligen Dateinamen der JPG-Erweiterung, der von der Funktion randomFileName() generiert wird. Wenn es uns gelingt, das Foto erfolgreich zu speichern, gibt das Skript den Remote-URL-Speicherort unseres neuen Fotos aus, andernfalls wird "false" ausgegeben, wenn dies fehlschlägt.
Jetzt müssen Sie die Datei upload.php speichern und auf Ihren Server übertragen. Darüber hinaus müssen Sie einen neuen Ordner im selben Verzeichnis wie Ihre upload.php-Datei mit dem Namen "uploads" erstellen. Dies ist der Ordner, in dem unsere Bilder gespeichert werden. Stellen Sie sicher, dass der Bildordner über die richtigen Schreibberechtigungen verfügt (normalerweise CHMOD 770). Andernfalls funktioniert unser PHP-Skript nicht.
Beachten Sie, dass Sie, wenn Sie keinen Zugriff auf einen PHP-Server haben, immer ein Skript in der Sprache Ihrer Wahl (.NET, Ruby usw.) schreiben können, das dieselbe Aufgabe ausführt.
Schritt 5: Hochladen eines Fotos und Anzeigen des Fortschritts über unsere benutzerdefinierte Fortschrittsleiste
Bevor wir unsere Funktion erstellen, müssen wir einige kleine Dinge tun. Die erste besteht darin, eine Variable namens androidUploadProgress oben in unserer exports.FirstView-Deklaration zu erstellen. Dadurch wird der ungefähre Upload-Fortschritt unseres Datei-Uploads auf Android-Geräten verfolgt. Leider unterstützt Titanium Mobile für Android zum Zeitpunkt des Schreibens die Fortschrittsvariable während des onsendstream-Ereignisses nicht, was bedeutet, dass wir nicht genau berechnen können, wie weit wir durch den Upload auf Android-Geräten sind, und daher einen Best-Guess-Ansatz verwenden müssen.
//this variable is for android to calculate the approx upload progress var androidUploadProgress = 0;
Außerdem müssen wir in der Datei "i18n/en/strings.xml" eine neue Zeichenfolgenvariable erstellen, die den Speicherort unseres Server-Upload-Skripts enthält. Fügen Sie das folgende Zeichenfolgenelement nach dem vorhandenen "welcome" hinzu und ersetzen Sie die URL durch den Speicherort Ihres eigenen PHP-Skripts.
<string name="server">http://mobiletuts.example.com/progressbar/upload.php</string>
Lassen Sie uns jetzt unsere UploadPhotoToServer-Funktion erstellen, indem Sie den folgenden Code in Ihre FirstView.js-Datei eingeben. Der beste Ort für diese Funktion befindet sich unter dem Abschnitt oben, in dem die Statusleiste auf iOS-Geräten ausgeblendet ist. Dies beginnt mit if(Ti.Platform.osname == 'iphone'. Diese Funktion überprüft, ob unser Benutzer online ist, und erstellt in diesem Fall einen neuen HTTPClient, der die Mediendaten an unseren PHP-Server sendet, wo sie gespeichert und die vollständige URL des hochgeladenen Bildes zurückgegeben werden.
Dies ist eine langwierige Funktion und bildet das "Fleisch" unserer Titananwendung. Lassen Sie uns sie also Stück für Stück durchgehen. Lassen Sie uns zunächst die Funktion selbst zusammen mit dem Anfangscode erstellen, der die Schaltfläche "Choose Photo" ausblendet, und dem Benutzer stattdessen unseren Fortschrittsbalken anzeigen:
//this function will take in a 'media' object (a photo from the gallery in this case) //and will upload it to our server via the PHP script. On a successful upload, our //server will return the new HTTP path of the image we uploaded, which we can then load //in the Safari/web browser so the user can view it. function UploadPhotoToServer(media){ if (Titanium.Network.online == true) { self.children[0].show(); //show the uploading slider progress bar self.children[0].children[0].width = 0; //make sure the default value is zero self.children[1].show(); //show the uploading label self.children[1].text = 'Uploading photo, please wait...'; //set the label to default value self.children[2].hide(); //hide the select photo button } else { alert('You must have a valid Internet connection in order to upload this photo.'); } }
Sie werden feststellen, dass wir auf unsere Anzeigekomponenten zugreifen, indem wir auf deren Index im untergeordneten Objekt von FirstView verweisen. Wir hätten unsere Anzeigekomponenten auch einfach oben in unserer FirstView.js-Datei deklarieren und dann instanziieren können. Wir haben uns jedoch dafür entschieden, die folgenden Schritte auszuführen, damit Sie verstehen, wie Sie auf das untergeordnete Element einer übergeordneten Ansicht zugreifen können. Als Nächstes müssen wir unser XHR-HttpClient-Objekt instanziieren und alle erforderlichen Ereignishandler erstellen, um zu verfolgen, wann unser HttpClient-Upload ausgeführt wird und ob er fehlschlägt oder erfolgreich war. Fügen Sie den folgenden Code unter dem zuvor eingegebenen hinzu. Beachten Sie, wie wir mit L("server") aus unserer Datei strings.xml auf die zuvor im Lernprogramm festgelegte Zeichenfolgeeigenschaft "server" zugreifen und wie das Parameterobjekt, das unser HttpClient-Beitrag sendet, "media" heißt und genau mit dem übereinstimmt "media" FILE-Objekt, das von unserem PHP-Skript erwartet wird.
var xhr = Titanium.Network.createHTTPClient(); xhr.onerror = function(e){ Ti.API.info('IN ERROR ' + e.error); alert('Sorry, we could not upload your photo! Please try again.'); }; xhr.onload = function(){ Ti.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState); }; xhr.onsendstream = function(e){ Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress); }; // open the client xhr.open('POST', L('server')); //the server location comes from the 'strings.xml' file // send the data xhr.send({ media: media, });
Bearbeiten wir nun die onsendstream-Ereignishandlerfunktion. In dieser Funktion berechnen wir den aktuellen Upload-Fortschritt unserer Datei und erweitern die Breite des Fortschrittsbalkens entsprechend. Beachten Sie, dass wir dies für Android etwas anders machen als für iOS, da die onsendstream-Eigenschaft "progress" derzeit für Android nicht implementiert ist.
xhr.onsendstream = function(e){ Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress); if(Ti.Platform.osname == 'android') { //android doesn't support the "progress" variable during onsendstream yet :( //we're going to dummy up a progress value for this based on each packet being about 2.5% of the total upload progress //it won't be totally accurate, but it will give the user a good indicator that the upload is working if(androidUploadProgress < 1) { androidUploadProgress += 0.025; self.children[1].text = 'Uploading photo, please wait... ' + (Math.round(androidUploadProgress * 100)).toString().replace(".","") + '%'; self.children[0].children[0].width = Math.round(298 * androidUploadProgress); } } else { //else on ios devices, calculate the progress of the upload using e.progress if(Math.round(e.progress * 100) <= 100) { self.children[1].text = 'Uploading photo, please wait... ' + (Math.round(e.progress * 100)).toString().replace(".","") + '%'; self.children[0].children[0].width = Math.round(298 * e.progress); //set the slider value to the nearest whole integer (ie 25%, not 24.95%) } } };
Versuchen Sie jetzt, Ihre Anwendung im Simulator auszuführen. Nachdem Sie ein Galerie-Foto ausgewählt haben, sollte Ihr Fortschrittsbalken angezeigt und beim Hochladen der Datei größer werden. Es sollte genauso aussehen wie das folgende:



Schritt 6: Fertig stellen
Schließlich müssen wir die Ereignishandlerfunktion zum Entladen erweitern. Darin müssen wir sicherstellen, dass der Fortschrittsbalken 100% anzeigt (insbesondere für Android), und wir müssen feststellen, ob die Datei erfolgreich auf den Server hochgeladen wurde oder nicht, indem wir überprüfen, ob die Eigenschaft responseText auf "false" gesetzt ist. Wenn dies nicht der Fall ist, können wir davon ausgehen, dass alles in Ordnung war, und unseren Benutzern einen Bestätigungsdialog anzeigen, in dem sie ihr neu hochgeladenes Bild im Webbrowser öffnen können. Wir setzen dann unser Layout und alle Objekteigenschaften zurück, wenn wir für den nächsten Foto-Upload bereit sind.
xhr.onload = function(){ Ti.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState); if(this.responseText != 'false') { var url = this.responseText; //set our url variable to the response self.children[0].children[0].width = 298; //set the progress to 100% (298px based on our design) //if we successfully uploaded, then ask the user if they want to view the photo var confirm = Titanium.UI.createAlertDialog({ title: 'Upload complete!', message: 'Open your image in the browser?', buttonNames: ['Yes', 'No'] }); confirm.addEventListener('click', function(conEvt) { //if the index selected was 0 (yes) then open in safari Ti.API.info(conEvt.index); if(conEvt.index === 0){ //open our uploaded image in safari Ti.Platform.openURL(url); } //reset the upload button self.children[0].hide(); //hide the status bar self.children[1].hide(); //hide the status label self.children[2].show(); //show the upload button again androidUploadProgress = 0; //reset the android progress value }); confirm.show(); } else { alert('Whoops, something failed in your upload script.'); self.children[0].hide(); //hide the status bar self.children[1].hide(); //hide the status label self.children[2].show(); //show the upload button again androidUploadProgress = 0; //reset the android progress value } };
Das ist es! Führen Sie die Anwendung jetzt entweder im Simulator (Android oder iPhone) oder auf Ihrem Gerät aus. Die Fortschrittsanzeige für das Hochladen von Dateien sollte weiterhin von 0 bis 100% animiert sein. Nach Abschluss sollte ein Warndialogfeld wie das folgende angezeigt werden, in dem Sie das neu hochgeladene Foto im Browser anzeigen können.





