Dit is het tweede deel van de serie over Audero Audiospeler. In dit artikel gaan we de bedrijfslogica van onze speler maken. Ik zal ook enkele van de Cordova API's uitleggen die in het vorige artikel zijn geïntroduceerd.
In deze sectie zal ik je de opgeroepen klasse laten zien Speler
, waarmee we kunnen spelen, stoppen, terugspoelen en snel vooruitspoelen. De klasse is sterk afhankelijk van de Media API; zonder zijn methoden zal onze speler volledig nutteloos zijn. Naast de Media API maakt deze klasse gebruik van de alert ()
methode van de meldings-API. Het uiterlijk van de waarschuwing verschilt per platform. De meeste ondersteunde besturingssystemen gebruiken een native dialoogvenster, maar andere, zoals Bada 2.X, gebruiken de klassieke browser alert ()
functie, die minder aanpasbaar is. De eerste methode accepteert maximaal vier parameters:
Houd er rekening mee dat Windows Phone 7 de naam van de knop negeert en altijd de standaardnaam gebruikt. Windows Phone 7 en 8 hebben geen ingebouwde browserwaarschuwing, dus als u wilt gebruiken alert ( 'e-mail');
, je moet toewijzen window.alert = navigator.notification.alert
.
Nu heb ik de API's uitgelegd die gebruikt worden door Speler
, we kunnen kijken hoe het gemaakt is. We hebben drie eigenschappen:
media
: de verwijzing naar het huidige geluidsobjectmediaTimer
: die een unieke interval-ID bevat die is gemaakt met de setInterval ()
functie die we zullen doorgeven clearInterval ()
om de timer van het geluid te stoppenspeelt
: een variabele die aangeeft of het huidige geluid wordt afgespeeld of niet. Naast de eigenschap heeft de klas verschillende methoden.De initMedia ()
methode initialiseert de media
eigendom met een Media
object dat het geluid weergeeft dat door de gebruiker is geselecteerd. Dit laatste wordt gemeld met behulp van de Meldings-API in geval van een fout. Het doel van de speel pauze
, hou op()
, en seekPosition ()
methoden moeten duidelijk zijn, dus ik ga verder. De resetLayout ()
en changePlayButton ()
methoden zijn heel eenvoudig. Ze worden gebruikt om de lay-out van de speler opnieuw in te stellen of bij te werken volgens de actie die door de gebruiker wordt uitgevoerd. De laatst overgebleven methode is updateSliderPosition ()
, die vergelijkbaar is met de schuifregelaar voor tijd. De laatste heeft nul (het begin van de schuifregelaar) als de standaardwaarde voor de huidige positie, ingesteld met behulp van de value = "0"
attribuut. Dit moet overeenkomstig worden bijgewerkt terwijl het geluid wordt afgespeeld om de gebruiker visuele feedback te geven over de verstreken speeltijd.
We hebben alle details van deze klasse gevonden, dus hier is de broncode van het bestand:
var Player = media: null, mediaTimer: null, isPlaying: false, initMedia: function (path) Player.media = new Media (pad, functie () console.log ('Mediabestand met succes gelezen'); if ( Player.media! == null) Player.media.release (); Player.resetLayout ();, function (error) navigator.notification.alert ('Kan het mediabestand niet lezen.', Function () , 'Fout'); Player.changePlayButton ('play'); console.log ('Kan het mediabestand niet lezen (Code):' + error.code);); , playPause: function (path) if (Player.media === null) Player.initMedia (pad); if (Player.isPlaying === false) Player.media.play (); Player.mediaTimer = setInterval (function () Player.media.getCurrentPosition (functie (positie) if (positie> -1) $ ('# media-gespeeld'). Text (Utility.formatTime (positie)); Speler .updateSliderPosition (positie);, functie (fout) console.log ('Kon mediapositie niet ophalen:' + error.code); $ ('# media-played'). text (Utility.formatTime (0) ););, 1000); var counter = 0; var timerDuration = setInterval (function () counter ++; if (teller> 20) clearInterval (timerDuration); var duration = Player.media.getDuration (); if (duration> -1) clearInterval (timerDuration); $ ('# media-duur '). text (Utility.formatTime (duration)); $ (' # time-slider '). attr (' max ', Math.round (duration)); $ (' # time-slider '). slider ('refresh'); else $ ('# media-duration'). text ('Unknown');, 100); Player.changePlayButton ( 'pause'); else Player.media.pause (); clearInterval (Player.mediaTimer); Player.changePlayButton ( 'play'); Player.isPlaying =! Player.isPlaying; , stop: function () if (Player.media! == null) Player.media.stop (); Player.media.release (); clearInterval (Player.mediaTimer); Player.media = null; Player.isPlaying = false; Player.resetLayout (); , resetLayout: function () $ ('# media-played'). text (Utility.formatTime (0)); Player.changePlayButton ( 'play'); Player.updateSliderPosition (0); , updateSliderPosition: function (seconds) var $ slider = $ ('# time-slider'); als (seconden < $slider.attr('min')) $slider.val($slider.attr('min')); else if (seconds > $ slider.attr ('max')) $ slider.val ($ slider.attr ('max')); else $ slider.val (Math.round (seconden)); $ Slider.slider ( 'refresh'); , seekPosition: function (seconds) if (Player.media === null) return; Player.media.seekTo (seconden * 1000); Player.updateSliderPosition (seconden); , changePlayButton: function (imageName) var background = $ ('# player-play') .css ('background-image') .replace ('url (', ") .replace (')',"); $ ('# player-play'). css ('background-image', 'url (' + background.replace (/ images \ /.* \. png $ /, 'images /' + imageName + '.png' ) + ')'); ;
Dit gedeelte illustreert de AppFile
klasse die wordt gebruikt om geluiden te maken, verwijderen en laden met behulp van de Web Storage API. Deze API heeft twee gebieden, Sessie en lokaal, maar Cordova gebruikt het laatste. Alle geluiden worden opgeslagen in een item met de naam "bestanden" zoals u kunt zien door naar de _tafel naam
eigenschappen.
Houd er rekening mee dat deze API alleen basisgegevens kan opslaan. Daarom zullen we, om te voldoen aan onze behoefte om objecten op te slaan, het JSON-formaat gebruiken. JavaScript heeft een klasse om dit formaat, genaamd JSON, aan te kunnen. Het maakt gebruik van de methoden ontleden ()
om een tekenreeks te ontleden en de juiste gegevens opnieuw te maken, en stringify ()
om het object in een string te converteren. Als laatste opmerking zal ik de puntnotatie van de API niet gebruiken, omdat Windows Phone 7 het niet ondersteunt, dus we gebruiken de setItem ()
en getItem ()
methoden om compatibiliteit voor alle apparaten te garanderen.
Nu u een overzicht hebt van hoe we de gegevens opslaan, laten we het hebben over de gegevens die we moeten opslaan. De enige informatie die we nodig hebben voor elk gevonden geluid is de naam (naam
eigenschap) en een absoluut pad (vol pad
eigendom). De AppFile
klasse heeft ook een "constante", genaamd EXTENSIONS
, waar we de extensies instellen die tegen elk bestand worden getest. Als ze overeenkomen, wordt het bestand verzameld door de toepassing. We hebben een methode om een bestand toe te voegen (bestand toevoegen()
), een methode om een bestand te verwijderen (Verwijder bestand()
), een methode die de hele database verwijdert (Verwijder bestanden()
), en, ten slotte, twee methoden die het bestand uit de database ophalen: getAppFiles ()
om alle bestanden op te halen, en getAppFile ()
om er maar een te halen. De klas heeft ook vier vergelijkingsmethoden, twee statische (vergelijken()
en compareIgnoreCase ()
) en twee niet-statische (vergelijk met()
en compareToIgnoreCase ()
). De laatste methode is de methode die wordt gebruikt om de index van een bepaald bestand op te halen, getIndex ()
. De AppFile
klasse stelt u in staat alle basisbewerkingen uit te voeren die u mogelijk nodig hebt.
De code die implementeert wat we hebben besproken, kan hier worden gelezen:
function AppFile (name, fullPath) var _db = window.localStorage; var _tableName = 'bestanden'; this.name = naam; this.fullPath = fullPath; this.save = functie (bestanden) _db.setItem (_tableName, JSON.stringify (bestanden)); this.load = function () return JSON.parse (_db.getItem (_tableName)); AppFile.prototype.addFile = function () var index = AppFile.getIndex (this.fullPath); var files = AppFile.getAppFiles (); if (index === false) files.push (this); else bestanden [index] = dit; this.save (bestanden); ; AppFile.prototype.deleteFile = function () var index = AppFile.getIndex (this.fullPath); var files = AppFile.getAppFiles (); if (index! == false) files.splice (index, 1); this.save (bestanden); bestanden terugzenden; ; AppFile.prototype.compareTo = function (other) return AppFile.compare (dit, andere); ; AppFile.prototype.compareToIgnoreCase = function (other) return AppFile.compareIgnoreCase (dit, andere); ; AppFile.EXTENSIONS = ['.mp3', '.wav', '.m4a']; AppFile.compare = function (appFile, other) if (other == null) return 1; else if (appFile == null) return -1; return appFile.name.localeCompare (other.name); ; AppFile.compareIgnoreCase = function (appFile, other) if (other == null) return 1; else if (appFile == null) return -1; retourneer appFile.name.toUpperCase (). localeCompare (other.name.toUpperCase ()); ; AppFile.getAppFiles = function () var files = new AppFile (). Load (); return (bestanden === null)? []: bestanden; ; AppFile.getAppFile = function (pad) var index = AppFile.getIndex (pad); if (index === false) return null; else var file = AppFile.getAppFiles () [index]; retourneer nieuwe AppFile (file.name, file.fullPath); ; AppFile.getIndex = function (pad) var files = AppFile.getAppFiles (); for (var i = 0; i < files.length; i++) if (files[i].fullPath.toUpperCase() === path.toUpperCase()) return i; return false; ; AppFile.deleteFiles = function() new AppFile().save([]); ;
De utility.js
bestand is erg kort en gemakkelijk te begrijpen. Het heeft maar twee methoden. Eén wordt gebruikt om milliseconden in een geformatteerde reeks te converteren die in de speler wordt getoond, terwijl de andere een JavaScript-implementatie is van de bekende Java-methode eindigt met
.
Hier is de bron:
var Utility = formatTime: function (milliseconds) if (milliseconds <= 0) return '00:00'; var seconds = Math.round(milliseconds); var minutes = Math.floor(seconds / 60); if (minutes < 10) minutes = '0' + minutes; seconds = seconds % 60; if (seconds < 10) seconds = '0' + seconds; return minutes + ':' + seconds; , endsWith: function(string, suffix) return string.indexOf(suffix, string.length - suffix.length) !== -1; ;
In dit gedeelte wordt het laatste JavaScript-bestand van het project besproken, application.js
, welke het bevat Toepassing
klasse. Het doel is om gebeurtenissen aan de elementen van de app-pagina te koppelen. Die evenementen zullen profiteren van de lessen die we tot nu toe hebben gezien en de speler in staat stellen om naar behoren te werken.
De code van de geïllustreerde functie staat hieronder:
var Toepassing = initApplication: function () $ (document) .on ('pageinit', '# files-list-page', function () Application.initFilesListPage ();); $ (document) .on ('pageinit', '# aurelio-page', function () Application.openLinksInApp ();); $ (document) .on ('pagechange', functie (event, properties) if (properties.absUrl === $ .mobile.path.makeUrlAbsolute ('player.html')) Application.initPlayerPage (JSON.parse ( properties.options.data.file));); , initFilesListPage: function () $ ('# update-button'). klik (functie () $ ('# waiting-popup'). popup ('open'); setTimeout (function () Application.updateMediaList ();, 150);); $ (document) .on ('endupdate', function () Application.createFilesList ('files-list', AppFile.getAppFiles ()); $ ('# waiting-popup'). popup ('close'); ); Application.createFilesList ('files-list', AppFile.getAppFiles ()); , initPlayerPage: function (bestand) Player.stop (); . $ ( '# Media-naam') tekst (file.name); . $ ( '# Media-path') tekst (file.fullPath); $ ('# player-play'). klik (function () Player.playPause (file.fullPath);); . $ ( '# Player-stop') klik (Player.stop); $ ('# time-slider'). on ('slidestop', functie (event) Player.seekPosition (event.target.value);); , updateIcons: function () if ($ (window) .width ()> 480) $ ('een [data-icoon], knop [data-icoon]'). each (function () $ (this ) .removeAttr ('data-iconpos');); else $ ('een [data-icoon], knop [data-icoon]'). each (function () $ (this) .attr ('data-iconpos', 'notext');); , openLinksInApp: function () $ ("a [target = \" _ blank \ "]"). on ('klik', functie (gebeurtenis) event.preventDefault (); window.open ($ (this) .attr ('href'), '_target');); , updateMediaList: function () window.requestFileSystem (LocalFileSystem.PERSISTENT, 0, function (fileSystem) var root = fileSystem.root; AppFile.deleteFiles (); Application.collectMedia (root.fullPath, true);, functie (fout) console.log ('Bestandssysteemfout:' + error.code);); , collectMedia: function (path, recursive, level) if (level === undefined) level = 0; var directoryEntry = new DirectoryEntry (", path); if (! directoryEntry.isDirectory) console.log ('Het opgegeven pad is geen map'); return; var directoryReader = directoryEntry.createReader (); directoryReader.readEntries ( function (entries) var appFile; var extension; for (var i = 0; i < entries.length; i++) if (entries[i].name === '.') continue; extension = entries[i].name.substr(entries[i].name.lastIndexOf('.')); if (entries[i].isDirectory === true && recursive === true) Application.collectMedia(entries[i].fullPath, recursive, level + 1); else if (entries[i].isFile === true && $.inArray(extension, AppFile.EXTENSIONS) >= 0) appFile = new AppFile (entries [i] .name, entries [i] .fullPath); appFile.addFile (); console.log ('Bestand opgeslagen:' + entries [i] .fullPath); , functie (fout) console.log ('Kan de map niet lezen. Errore:' + error.code); ); if (level === 0) $ (document) .trigger ('endupdate'); console.log ('Huidig geanalyseerd pad is:' + pad); , createFilesList: function (idElement, bestanden) $ ('#' + idElement) .empty (); if (files == null || files.length == 0) $ ('#' + idElement) .append ('Geen bestanden om te laten zien. Overweegt u een update van bestanden (knop rechtsboven)?
'); terug te keren; functie getPlayHandler (bestand) return function playHandler () $ .mobile.changePage ('player.html', data: file: JSON.stringify (file)); ; functie getDeleteHandler (bestand) return function deleteHandler () var oldLenght = AppFile.getAppFiles (). length; var $ parentUl = $ (this) .closest ('ul'); file = new AppFile (", file.fullPath); file.deleteFile (); if (oldLenght === AppFile.getAppFiles (). length + 1) $ (this) .closest ('li'). remove () ; $ parentUl.listview ('refresh'); else console.log ('Media niet verwijderd. Er is iets verkeerd gegaan.'); navigator.notification.alert ('Media niet verwijderd. Er is iets misgegaan dus probeer het opnieuw'. , function () , 'Error');; var $ listElement, $ linkElement; files.sort (AppFile.compareIgnoreCase); for (var i = 0; i < files.length; i++) $listElement = $('
In het vorige deel van deze serie vermeldde ik dat een interessant punt op de aftitelingpagina het attribuut was target = "_ blank"
toegepast op de links. In dit gedeelte wordt uitgelegd waarom openLinksInApp ()
methode van de Toepassing
klasse is logisch.
Eens gebruikte Cordova externe links in dezelfde Cordova WebView waarop de applicatie draaide. Wanneer een link open was en de gebruiker op de knop 'Terug' klikte, werd de laatst weergegeven pagina precies zo weergegeven als voordat de gebruiker de link had verlaten. In de nieuwere versie is dit veranderd. Tegenwoordig worden de externe koppelingen standaard geopend met de Cordova WebView als de URL zich op de witte lijst van uw app bevindt. URL's die niet op uw whitelist staan, worden geopend met de InAppBrowser-API. Als u de koppelingen niet op de juiste manier beheert, of als de gebruiker op een koppeling tikt die wordt weergegeven in de InAppBrowser of het systeem en vervolgens kiest om terug te gaan, gaan alle verbeteringen van jQuery Mobile verloren. Dit probleem treedt op omdat de CSS- en JavaScript-bestanden worden geladen door de hoofdpagina en de volgende worden geladen met AJAX. Voordat we de oplossing blootleggen, laten we eens kijken wat de InAppBrowser is.
De InAppBrowser is een webbrowser die in uw app wordt weergegeven wanneer u de call window.open gebruikt.
Deze API heeft drie methoden:
addEventListener ():
Hiermee kunt u luisteren naar drie evenementen (loadstart
, loadstop
, en Uitgang)
en voeg een functie toe die wordt uitgevoerd zodra deze gebeurtenissen worden geactiveerdremoveEventListener ()
: Verwijdert een eerder gekoppelde luisteraar.dichtbij()
: Wordt gebruikt om het venster InAppBrowser te sluiten.Dus, wat is de oplossing? Het doel van de openLinksInApp ()
De functie, gekoppeld aan de whitelist die is opgegeven in het configuratiebestand, is om de klikken op alle externe links te herkennen die worden herkend door het gebruik van de target = "_ blank"
attribuut, en open ze met behulp van de window.open ()
methode. Met deze techniek zullen we het beschreven probleem vermijden en zal onze speler blijven kijken en werken zoals verwacht.
In de derde en laatste aflevering van deze serie zien we de laatst overgebleven bestanden zodat je het project kunt voltooien en er mee kunt spelen.