Visoka poslovna škola strukovnih studija
Blace
Specijalistički rad
Hibridne mobilne aplikacije
Marijana Nicić
U Blacu, 2015. God.
Visoka poslovna škola strukovnih studija
Blace
Hibridne mobilne aplikacije
-Specijalistički rad-
Kandidat: Marijana Nicić Mentor: Ivan Stanković
U Blace, 2015. god.
Sadržaj
1. Uvod...................................................................................................................................4
2. Vrste mobilnih aplikacija prema načinu razvoja.................................................................6
2.1. Native aplikacije .........................................................................................................6
2.2. Web aplikacija.............................................................................................................8
2.3. Hibridne aplikacije.......................................................................................................9
3. Framework.......................................................................................................................11
4. AngularJS.........................................................................................................................12
4.1. AngularJS komponente............................................................................................13
4.2. AngularJS direktive...................................................................................................13
4.3. AngularJS servisi......................................................................................................14
5. Apache Cordova..............................................................................................................15
6. IONIC...............................................................................................................................17
6.1. Struktura IONIC projekta..........................................................................................17
6.2. IONIC CSS komponente ........................................................................................18
6.3. IONIC direktive i servisi............................................................................................19
7. IONIC aplikacija sliding-puzzle........................................................................................25
8. Zaključak..........................................................................................................................49
9. Literatura..........................................................................................................................51
1. Uvod
Mobilne aplikacije su aplikacijski softver napravljen da radi na pametnim telefonima,
tablet računarima i drugim mobilnim uređajima. Aplikacije su proizvodi koji su najbitniji
krajnjim korisnicima. Njima su na raspolaganju sav hardver uređaja i sve opcije koje nudi
sam operativni sistem. Mogućnosti aplikacija zavise od operativnog sistema tj. od toga
koje opcije on podržava. Aplikacije mogu koristiti u kombinaciji više mogućnosti koje im
pruža operativni sistem kako bi iskoristili hardverske komponente uređaja. Rad svake od
aplikacija se zasniva na razmeni informacija sa osnovnim softverskim alatima koji su
integrisani u operativni sistem.
Mobilne aplikacije su isprva bile pretežno zabavnog karaktera ili jednostavne
aplikacije za osobnu organizaciju, a s vremenom su pronašle razne funkcionalne primene
kako u poslovnom svetu tako i u ličnoj upotrebi. Tako danas uz lokacijski bazirane servise,
društvene mreže, brojne reklamne i slične smartphone programe, mobilne aplikacije
nalaze svoju primenu i u raznim poslovnim procesima unutar kompanije (terenski i
dislocirani rad, prikupljanje i deljenje informacija, nadzor i upravljanje).
Mnoge mobilne aplikacije dolaze već instalirane na samim uređajima. Tržište
aplikacija koje je moguće preuzeti sa marketa konstantno raste zajedno sa brojem
developera mobilnih aplikacija, kompanijama koje ih objavjuju, kao i provajderima koji ih
prodaju. Dostupne na svim telefonima današnjice, mobilne aplikacije predstavljaju jedan
od segmenata globalnog mobilnog tržišta sa najvećim i najbržim rastom. Njihova namena
može da varira, počevši od osnovnih funkcionalnosti mobilnih telefona kao što su razgovor
i slanje poruka, pa sve do igranja video igrica ili naprednijih servisa kao što su pretraga
interneta i gledanje video sadržaja.
Pre početka razvoja mobilne aplikacije koja treba da se izvršava na različitim
mobilnim platformama, neophodno je definisati njene funkcionalnosti, jer postoje razlike u
dostupnosti određenih funkcionalnosti na različitim platformama. Veoma često se u
aplikacijama koriste neke opšte funkcionalnosti koje su dostupne na svim platformama.
Svaka platforma pruža određene funkcionalnosti i uglavnom nije moguće koristiti istu
aplikaciju na različitim platformama.
Upravo veliki broj različitih platformi i veliki broj različitih tehnologija za razvoj
mobilnih aplikacija dovode do toga da razvoj aplikacija za različite platforme zahteva više
vremena, pa se samim tim i troškovi razvoja povećavaju. Osim toga, u slučaju izmene dela
aplikacije potrebno je izvršiti identične ili slične promene u izvornom kodu svih varijanti
aplikacije pisanih za svaku pojedinačnu platformu.
Dakle, jedna aplikacija ima više različitih varijanti izvornog koda za različite
platforme, pisane u različitim programskim jezicima. Svi ovi problemi su doveli do ideje da
se aplikacije razvijaju na univerzalan način, pomoću jedne tehnologije, a da se uz
minimalna prilagođavanja mogu izvršavati na više platformi. Realizacijom ove ideje
smanjuju se troškovi razvoja što u poslovnom smislu dovodi do većeg finansijskog
„uspeha“ aplikacije. U tehničkom smislu sam razvoj se pojednostavljuje, a promene u
funkcionalnostima aplikacije postaju jednostavnije, jer se jedan izvorni kod koristi za sve
platforme.
2. Vrste mobilnih aplikacija prema načinu razvoja
Aplikacije mogu biti native, web, ili hibridne, u zavisnosti od toga koji pristup za
izradu je izabran. Izbor određenog pristupa nije jednostavan i zavisi od više parametara
koji se moraju razmotriti pre početka razvoja, a odnose se na tehničke i ekonomske
aspekte, kao i na ograničenja pri razvoju. Tehnički apekti se odnose na funkcionalnosti
uređaja koje se mogu koristiti u aplikaciji, neophodne performanse aplikacije, kao i dizajn.
Ekonomski aspekti su određeni vremenom i troškovima razvoja, kao i mogućnošću
korištenja iste aplikacije na većem broju platformi.
2.1. Native aplikacije
Native aplikacije razvijaju se za upotrebu na određenoj platformi. Kada se aplikacija
instalira na uređaj, ona komunicira sa operativnim sistemom uređaja, a može da koristi sve
funkcionalnosti uređaja, jer se njen razvoj bazira na kompletnom API-ju (Application
Programming Interface). Osim toga, ovakve aplikacije koriste ugrađene grafičke
komponente i imaju dizajn koji je karakterističan za platformu na kojoj se aplikacija
izvršava. Aplikacije se preuzimaju i smeštaju na fajl sistem mobilnog uređaja, a njihova
distribucija obično se vrši preko „trgovina“ aplikacijama za određenu platformu.
Razvoj native aplikacija se vrši pomoću softverskih alata za izabranu platformu. Kod
aplikacije se piše u jednom od programskih jezika koji su predviđeni za razvoj aplikacija na
datoj platformi. Napisani izvorni kod se kompajlira i povezuje sa potrebnim bibliotekama
pomoću SDK (Software Development Kit) alata koji dostavlja proizvođač platforme.
Izvršnoj verziji programa dodaju se resursi koji se koriste u aplikaciji, i tako zapakovana
aplikacija postavlja se na „trgovinu“ aplikacijama.
Native aplikaciju moguće je razviti i upotrebom alata za razvoj aplikacija za više
platformi. Pri tome, moguće je koristiti skup zajedničkih funkcionalnosti svih izabranih
platformi, ili se aplikacija razvija posebno za svaku platformu nakon čega se vrši
kompajliranje, tipično HTML5 i JavaScript koda aplikacije u native format. Razvoj native
aplikacija prikazan je na slici 1.
Slika 1: Razvoj native apikacije
Native aplikacije se izvršavaju pod kontrolom operativnog sistema uređaja, bez bilo
kakvog dodatnog kontejnera ili sloja. Pristup funkcionalnostima uređaja se vrši pomoću
API-ja operativnog sistema (slika 2).
Prednosti native aplikacija ogledaju se u mogućnosti upotrebe svih funkcionalnosti
koje obezbjeđuje operativni sistem, boljim performansama kod zahtevnijih aplikacija i
standardizovanom izgledu aplikacija na određenoj platformi. Osnovni nedostatak native
aplikacije jeste mogućnost njenog korištenja na samo jednoj platformi.
Slika 2: Izvršenje native aplikacije
2.2. Web aplikacija
Web aplikacije zasnovane su na web tehnologijama: HTML5, CSS i JavaScript, uz
mogućnost korištenja dodatnih biblioteka i softverskih okruženja kao što je jQuery.
Aplikacije se mogu instalirati na uređaj, ali to nije obavezno. Pokretanje web aplikacija vrši
se unosom URL adrese, aktiviranjem hyperlink-a ili pokretanjem prečice. Za razliku od
native aplikacija, web aplikacije ne izvršava direktno operativni sistem uređaja, već se ove
aplikacije izvršavaju u okviru browser-a, kao što je prikazano na slici 3.
Slika 3: Izvršavanje web aplikacija
Zbog toga web aplikacija nema pristup celom API-ju operativnog sistema, već samo
onom delu koji je dostupan iz browser-a, koji je i sam deo API-ja. Kao što je prikazano na
slici 3, web aplikacija se izvršava u dodatnom modulu koji može ostvariti API pozive prema
operativnom sistemu.
Osnovna prednost web aplikacija jeste njena prenosivost - jednom napisana
aplikacija može se izvršavati na svim platformama. Kako se za razvoj web aplikacija
koriste HTML5, CSS i JavaScript, nije potrebno učiti tehnologije koje se koriste za razvoj
na određenoj platformi. Ovo dovodi do uštede resursa i vremena. Pored toga, web
aplikacije nemaju update, a uvek se koristi aktuelna verzija.
Nedostaci web aplikacija su nešto lošije performanse pri izvršavanju u odnosu na
native aplikacije, iako je to u poslednje vreme sve manje izraženo zbog poboljšanja
komponenti samih uređaja, upotrebe JIT (Just-In-Time) kompajlera i novih standarda kao
što je WebGL (Web Graphics Library) za razvoj igrica.
Najveći nedostatak su ograničenja u funkcionalnostima koje se mogu realizovati,
zbog toga što se ne može pristupiti celom API-ju operativnog sistema. Pored toga, web
aplikacije se ne objavljuju na trgovinama aplikacija.
2.3. Hibridne aplikacije
Kombinacijom pristupa razvoja native i web aplikacija nastale su hibridne aplikacije.
Kod ovakvog pristupa deo aplikacije se razvija kao i u slučaju web aplikacije - u
HTML5, CSS i JavaScript-u, dok se pristup API-ju operativnog sistema vrši pomoću
dodatnih native delova, koji su prilagođeni da se mogu koristiti pomoću web tehnologija.
Native delovi se mogu samostalno razvijati, a mogu se koristiti gotova rešenja. Deo
hibridne aplikacije koji se razvija pomoću web tehnologija funkcioniše na isti način kao i
kod web aplikacija - pokreće se i izvršava pomoću browser-a i ima ograničen pristup API-
ju operativnog sistema. Native deo aplikacije ima potpun pristup API-ju operativnog
sistema, i povezuje se sa delom za obradu web komponenti aplikacije (browser-om). Na
ovaj način aplikacija ostvaruje native funkcionalnosti, što je i prikazano na slici 4
Slika 4: Izvršavanje hibridnih aplikacija
Na slici 5 prikazan je proces razvoja hibridne aplikacije. Kao što se vidi, on je
veoma sličan procesu razvoja native aplikacija, uz dodatak da se deo aplikacije razvijen
pomoću web tehnologija dodaje u resurse aplikacije, a može se preuzimati i sa servera.
Ukoliko je deo aplikacije dostupan preko servera, aplikacija gubi osobinu da je dostupna
ukoliko nije povezana na Internet, ali se olakšava promena delova aplikacije.
Slika 5: Razvoj hibridnih aplikacija
Prednosti razvoja hibridnih aplikacija su mogućnost korištenja celog API-ja
operativnog sistema, razvijanje aplikacije na uniforman način (pomoću web tehnologija) i
mogućnost distribucije na više platformi.
Nedostaci se ogledaju u tome što se moraju poznavati tehnologije za razvoj native
delova koje su različite za svaku platformu. Bitno je napomenuti da su moguća i
ograničenja u pristupu funkcijama uređaja, ukoliko one nisu podržane u native delu
aplikacije.
3. Framework
Da bi aplikacija izgledala organizovano, uredno i profesijonalno po najnovijim
principima programiranja većina programera se odlučuje za već gotova rešenja kao što su
različiti framework. Framework je skup različitih klasa koje sadrže veliki broj različitih
funkcija.
Neki od najčešće korišćenih Framework-a za izradu hibridnuh aplikacija su :
ONSEN UI je relativno nov ali je već konkurencja za IONIC. ONSEN UI je
open sorce i dostupan je u okviru apache licnce. ONSEN UI je optimizovan
za korišćenje angular direktiva i topcoat framework-a za većinu UI
komponenti.
INTEL XDK je malo drugačiji od ostalih on uključuje kompletnu ponudu alata
koji podržavaju razvoj, emulaciju, testiranje, odklanjanje grešaka i
objavljivanje hibridnih HTML5 aplikacija.
SENCHA TOUCH je vodeći MCV bazirani JavaScript framework za izgradnju
hibridnih mobilnih aplikacija. Ovaj framework uključuje složen paket
podataka koji mogu da koriste podatke iz bilo kog izvora podataka. Sencha
Touch framework dolazi sa izvornim izgledom tema ze sve mobilne
platforme.
KENDO UI je framework za izradu bilo kakve web aplikacije ili hibridne
mobilne aplikacije korištenjem HTML5, JavaScript i CSS. Kendo UI poseduje
teme spremne za upotrebu.
Framework 7 je vodeći framework za izradu iOS aplikacija. Postoji mnogo UI
elemenata koji su spremni za korišćenje.
MOBILE ANGULAR UI omogućava korišćenje Bootstrap3 i Angular
framework-a za izradu HTML5 mobilnih aplikacija.
MONACA je komercijalna platforma za izradu mobilnih aplikacija. Monaca
čini proces razvoja hibridnih aplikacija korišćenjem PhoneGap/Cordova
lakšim.
4. AngularJS
IONIC je jedan od najčešće korišćenih framework-a, da bi smo dobro razumeli
IONIC potrebno je da dobro razumemo AngularJS.
AngularJS ili kraće samo Angualar je framework za razvoj web aplikacija pod
OpenSource licencom a razvija ga Google kao i zajednica programera koji zajedno rade
na unapređenju kao i mnogim izazovima koje donosi razvoj web aplikacija na jednoj
stranici (Single page applications). Cilj je olakašanje razvoja ali i testiranje web aplikacija
time što pruža framework koji se pridržava pravila Model-View-Controller arhitekture
pružajući dodatne mogućnosti pri stvaranju aplikacija.
Ideja na kojoj se AngularJS zasniva je umanjiti broj transakcija između servera i
browser-a odnosno smanjiti broj paketa koji se šalje više puta, ili da pojasnim osnova
aplikacije tj HTML i njegovi dodaci kao što su .js i drugo, budu učitani samo pri prvom
otvaranju aplikacije, pri kliku na neki od linkova na stranici koju ste prvi piut otvorili ka
serveru se šalje zahtev koji sadrži informacije o stranici koju zahtevate ali se umesto
kompletnog HTML i .js koda vraća samo json paket koji sadrži informacije koje će vam biti
prikazane a HTML i .js su već smešteni u memoriji pa je samim tim otvaranje stranica
gotovo neprimetno i brzo, samim tim je sačuvano mnogo nepotrebnih razmena između
vašeg browser-a i servera na kome se nalazi sajt koji posećujete, ali i ubrzava rad samih
sajtova a time otvaraju nove mogućnosti za razvoj kako novih vrsta aplikacija tako i
potpuno unapređenje postojećih aplikacija i sajtova.
Za početak razvoja aplikacija/sajtova upotrebom AngularJS-a potrebno je
poznavanje HTML-a i JavaScript-a. Osnova svake AngularJS aplikacije je JavaScript fajl
koji google hostuje a kojeg jednostavnim unosom dodajete na vašu webstranicu ili
aplikaciju.
AngularJS je napravljen u veri da deklarativno programiranje treba da se koristi za
pravljenje korisničkih interfejsa i povezivanje softverskih komponenti, dok imperativno
programiranje više odgovara za definisanje aplikacione biznis logike. Framework
prilagođava i proširuje tradiocinlani HTML kako bi predstavio dinamički sadržaj kroz
dvosmerno vezivanje podataka koje omogućava automatsku sinhronizaciju modela i view-
a u MCV arhitrkturi.
AngularJS je dizajniran sa ciljevima da:
Razdvoji DOM manipulacije od aplikacione logike. Na teškoću ovoga dramatično utiče
način na koji je kod struktuiran.
Razdvoji klijentsku stranu aplikacije od serverske strane aplikacije. Ovo omogućava
rad na razvoju obe strane paralelno, i omogućava ponovnu upotrebu obe strane
Obezbedi strukturu za redosled razvoja aplikacije – od projektovanja korisničkog
interfejsa, preko pisanja poslovne logike do testiranja
Angular implementira MVC arhitekturu radi razdvajanja komponenti za prezentaciju,
podatke i logičkih komponenti. Koristeći zavisnost paketa, Angular donosi tradicionalne
serverske servise, kao što su kontroleri koji zavise od prezentacionog sloja, na web
aplikacije na klijentskoj strani. Shodno tome, veliki deo tereta na serveru se može smanjiti.
4.1. AngularJS komponente
AngularJS se definiše iz HTML-a za razliku od većine JavaScript framework-a. U
tipičnim web stranicama AngularJS vodi računa o povezivanju ključnij delova koda. Dakle
ako dodamo gomilu AngularJS direktno na HTML stranicu i uključimo AngularJS izvorni fajl
možemo lako izraditi aplikaciju bez pisanja JavaScript koda.
Jedna od AngularJS komponenti su atributi koju počinju sa ng i nazivaju se
AngularJS directives. Naredna komponenta AngularJS naziva se Dependency Injection
(DI). DI brine o ubacivanju grupe koda gde je to potrebno, to je jedan od ključnih adaptera
koji pomažu da se ostvari razdvajanje problema.
4.2. AngularJS direktive
AngularJS direktive su jako korisna opcija neke od direktiva su:
ng_app: ovo pokrće novi podrazumevani AngularJS modul kada ne postoje
vrednosti, u suprotnom pokreće imenovani model.
ng_init: inicijalizuje vrednosti aplikacije.
ng_include: sluzi za uključivanje HTML koda u aplikaciju.
ng-controller: definiše kontrolu objekta iz aplikacije.
ng-click: definiše ponašanje kada se klikne na element.
ng_model: ovo mapira vrednosti ulaznog elementa na trenurni.
ng_show: ovo pokazuje DOM elemente kada je izraz prilagođen na
ng_show is true.
ng_hide: ovo skriva DOM elemente kada je izraz postavljen na ng_hide is
true.
ng_repeat: vrši prelaz kroz petlju na jednostavan način.
4.3. AngularJS servisi
AngularJS servisi služe za zamenu objekata koji se ne mogu ubaciti u direktive i
kotrolere preko DI (Dependency Injection). Ovi objekti se sastoje od jednostavnih
komanda poslovne logike koji se mogu koristiti preko aplikacije.
AngularJS servisi se “lenjo” inicijalizuju samo kada je komponenta zavisna od njih.
To čini servise savršenim ze rezmenu podataka između kontrolera i njihovo zadržavanje u
memoriji ako je to potrebno. Svaka komponenta zavisi od dodeljenih servisa.
Neki od AngularJS servisa su:
$animate omogućava niz kompletnih DOM metoda koje pruzaju podršku za
animacije.
$controller servis omogućava kontrolu.
$http je osnovni AngularJS servis koj služi za olakšanu komunikaciju sa udaljenim
serverom.
$locale servis pruža lokalizacije pravila za različite Angular komponente.
$location analizira ULR adresu u pretraživaču i čini je dostupnom aplikaciji.
5. Apache Cordova
Apache Cordova je deo softvera koj omogućava kombinovanje web aplikacije i
native aplikacije. Pored toga što omogućava kombinovanje ova dva načina razvoja
aplikacije Apache Cordova takođe pruža i API pakete pisanih u JavaScript-u za integraciju
sa mobilnim uređajem.
U kombinaciji sa UI framework-om poput jQuery Mobile, Dojo Mobile, Sencha
Touch ili IONIC omogućava da aplikacije za mobilne uređaje budu razvijene isključivo
korištenjem programskih jezike HTML5, CSS i JavaScript. Kada se koristi Cordova
aplikacije za mobilne uređaje mogu se razvijati bez i jedne linije koda nativnih programskih
jezika (Java, Objektni C, itd.). Umesto toga, koriste se web tehnologije, koje se same
predstavljaju lokalno na mobilnom uređaju unutar aplikacije (generalno nisu predstavljene
na nekom udaljenom http serveru). Zbog tih JavaScript načina programiranje mobilne
aplikacije su konzistentne na višestrukim platformama i izrađene prema web standardima,
tako da bi aplikacije trebale biti portabilne na svim mobilnim platformama (Apple iOS,
Google Android, Microsoft Windows Phone, BlackBerry, LG webOS) sa minimalnim ili bez
promena u pisanju koda.
slika6: način na koj funkcijoniše cordova
Apache Cordova se sastoji od sledećih komponenti:
izvorni kod za native kontejner za svaku od mobilnih platvormi
paket API koj omogućava web aplikaciji da radi u okviru native kontejnera i
omogućava iskorištenje kapaciteta uređaja koj obično nije podrzan od strane
mobilnog pretraživača.
Paket alata koj može da podrži proces kreiranja aplikacije.
6. IONIC
IONIC je open source SDK za izradu hibridnih moblinih aplikacija uz pomoć HTML5.
IONIC omogućava HTML, CSS i JavaScript komponente takođe omogućava i alate za
izradu interaktivnih aplikacija. IONIC koristi AngularJS kao svoj JavaScript framework, sa
snagom AngularJS unutar framework-a kao što je IONIC mogućnosti su neograničene.
IONIC ima veoma dobru integraciju sa Cordova API, to znači da se može pristupiti API-ju
uređaja korišćnjem biblioteka poput ngCordova.
Ne samo da Ionic izgleda lepo nego je i njegova temeljna arhitektura napravljena za
jako ozbiljan razvoj bilo kakvih aplikacija. IONIC je prvenstveno napravljen da programeri
jako lako razumiju kako stvari izgledaju na IOS i Android uređajima ali također je pogodan
za pravljenje hibridnih aplikacija koje jednako dobro rade i na bilo kojem PC-u kao i na
mobilnim telefonima bilo kojeg OS-a. Politika IONIC-a je "Čisto, Jednostavno,
Funkcionalno" A što je najbolje, sve što trebate znati je osnove CSS-a, HTML-a i
JavaScript-a da bi stvarno počeli praviti vašu prvu, potpuno funkcionalnu aplikaciju.
U poslednjih nekoliko godina IONIC je postao vodeći framework za razvoj hibridnih
mobilnih aplikacija.
6.1. Struktura IONIC projekta
bower.json: u ovom fajlu su dostupne sve informacije o brower paketu koji se
koristi u aplikaciji.
config.xml: ovoj fajl sadrži cordova informacije potrebne ze pretvaranje IONIC
aplikacije u instaler za određenu platformu. config.xml fajlu se nalazi mnogo xml tagova
koji opisuju projekat.
gulpfile.js: ovaj fajl se sastoji od zadataka koji ce se koristiti prilikom izrade
aplikacije.
ionic.project: u ovom fajlu se nalaze informacije koje su vežne za aplikaciju.
Hoocks: u ovom folderu se nalaze skripte koje se izvršavaju kada se vrši određeni
cordova zadatak. Neki od cordova zadataka su: after_platform_add, after_plugin_add,
before_emulate, after_run.
plugins: ovaj folder se sastoji od svih dodataka (plugin) dodatih projektu.
scss: ovaj folder se sastoji od osnovnog SCSS fajla koji služi za prilagođavanje
IONIC komponenti.
www: u ovom folderu se nalazi IONIC kod. Sve što je napisano nalazi se tu.U
okviru www foldera nalazi se: index.html: ovo je fajl koj startuje aplikaciju; css: u ovom
folderu se nalaze stilovi koji su specifični za aplikaciju; img: folder u kome se nalaze slike
koje se prikazuju u aplikaciji; js: u ovom folderu se nalazi JavaScript kod specifičan za
aplikaciju; lib: ovo je folder gde su postavljeni svi paketi prilikom pokretanja instalacije.
6.2. IONIC CSS komponente
IONIC je kombinacija CSS framework-a i mnogo AngularJS direktiva i servisa.
IONIC CSS framework sadrži većinu komponenti koje su potrebne ze izradu aplikacija.
Header služi za postavljanje fiksnog zaglavlja na vrhu ekrana, može da sadrži
naslov strane i dugmiće za navigaciju levo-desno ili za obveštenja i mnoge druge
aktivnosti.
Footer služi za postavljanje fiksnog podnožja i može da sadrži mnoge funkcije.
Content omogućava stilizovanje sadržaja koj se nalazi izmeđ zaglavlja i podnožja.
Dok su podnožje i zaglavlje fiksni sadržaj koj se nalazi između njih može da se pomera a
to je moguće definisati pomoću ove funkcije.
Cards je jedan od najbolje dizajniranih šablona za prikazivanje sadržaja na
mobilnim uređajima. Za svaku stranicu ili aplikaciju koja prikazuje personalizovani sadržaj
Cards je odličan izbor.
Grid System služi za dobijanje finalnog izgleda, postavljanjem komponenti ili
poravnavanje elemenata na stranici. Lepota IONIC grid sytem-a je u tome što je baziran
na FlexBox modula koj omogućava optimizaciju izgleda korisničkog intefejsa. Prednosta
grid system-a baziranog na FlexBox je u tome što nemora da postoje fiksne kolone, u
okviru reda moze se definisati više kolona i one će se same prilagoditi.
Button IONIC omogućava različite varijacije stila i veličine za dugmiće. Dugmići su
bitan deo mobilne aplikacije. Neki od stilova koji mogu biti dodeljeni dugmićima su: button-
block, button-full, button-large, button-outline, button-clear, button-bar.
Toggle je isto čto i HTML checkbox input osim čto drugačije izgleda i lakše je za
koriščenje u mobilnim aplikacijama.
LIST je glavna komponenta za svaku aplikaciju koja prikazuje neku listu. Struktura
liste je prilično jednostavna, kao i sve ostale IONIC komponente izrađena je uz pomoć
CSS klase i HTML strukture. item-divider služi za grupisanje liste. item-icon služi za
postavljanje ikone u listu. item-button omogućava postavljanje dugmića u listu. item-avatar
služi za postavljanje slike u listu koja je malo veća u odnosu na dugmiće. item-thumbnail
omogućava postavljanje slike u listu koja je veca od ikone.
IONIC omogućava različite korisne klase koje pomazu u brzom stilizovanju
aplikacije. Jedna od tih klasa je padding:
padding: određivanje jednokog rastojanja od margina na celoj strani.
padding-vertical: određuje rastojanja od margina na vrhu i na dnu strane.
padding-horizontal: određuje rastojanje od magina levo i desno na stranici.
padding-top: određuje rastojanje od margine na vrhu stranice.
padding-right: određivanje rastojanja od margine na desnoj strani.
padding-bottom: određivanje rastojanja od margine na dnu strane.
padding-left: određivanje rastojanja od margine na levoj strani.
6.3. IONIC direktive i servisi
IONIC ima komponente koje su direktno uvezene iz CSS a ima i komponente
kojima je potrebana magija JavaScript-a da bi se uptpunile. Pošto IONIC koristi AngularJS
kao svoj framework sve komponente za višestruku upotrebu korisničkog interfrjsa biće
napisane u obliku direktiva, a svi delovi JavaScript-a koje treba primenjivati više puta biće
napisani u formi servisa.
Neke od IONIC direktiva su:
Navigation (nav-direction, ion-nav-title, ion-view, ion-nav-view, ion-nav-bar,
ion-nav-button, ion-nav-back-button),
Content (ion-content, ion-pane i ion-refresher),
Header (ion-header-bar),
Footer (ion-footer-bar),
Lists (ion-list, ion-item i collection-repeat),
Tabs (ion-tabs i ion-tab),
Side menu (ion-side-menus i ion-side-menu),
Form Input (ion-checkbox, ion-radio i ion-toggle),
Gestures, Events (on-hold, on-tap, on-double-tap, on-touch, on-release on-
drag, on-swipe),
Keyboard (keyboard-attach, hide-on-keyboard-open),
Model (ionicModal),
Scroll (ion-scroll, ion-infinite-scroll),
Spinner (ion-spinner)
Navigation (nav-direction, ion-nav-title, ion-view, ion-nav-view, ion-nav-bar,
ion-nav-button, ion-nav-back-button)
ion-nav-view ova direktiva se koristi za prikazivanje sadržaj infomacija. ion-nav-bar
direktiva se koristi za pravljenje menija za navigaciju kroz aplikaciju. ion-nav-buttons se
koristi za postavljanje dugmića u zaglavlje. ion-nav-back-button ova direktiva služi za
automacko postavljanje dugmeta za povratak. ion-view omogućava prikazivanje
informacija u zaglavlju. ion-nav-title direktiva koja omogućava postavljanje željenog
naslova u zaglavlje. nav-direction direktiva služi za podešavanje pravca u kome će se
vršiti animacija.
Content (ion-content, ion-pane and ion-refresher)
ion-content direktiva se koristi za određivanje područija sadržaja, ova direktiva ima gomilu
opcija koje omogućavaju bolju kontrolu. Neki od ključnih atributa ion-content direktive su:
scroll (odeđuje dali je moguće pomeranje sadržaja); on-scroll (upravlja akcima koje ce se
desiti kada se sadržaj pomeri); on-scroll-complete (prikazuje sadržaj kada je pomeranje
završeno); scroll-event-interval (vreme koje je potrebno da prođe da bi se izvršila funkcija
on-scroll); scroll-x (horizontalno pomeranje); scroll-y (vertikalno pomeranje); locking
(zaključavanje ekrana); direction (pokazuje pravac u kome se vrši pomeranje).
Ion-refresher direktiva koja osvezava sadržaj.
Header (ion-header-bar) i Footer (ion-footer-bar)
Korišćenje ion-header-bar direktive služi za postavljanje fiksong zaglavlja, ion-footer-bar
služi za dodavanje fiksnog podnožja.
Lists (ion-list , ion-item i collection-repeat )
Liste su jedan od najčešće korišćenih šablona kada radimo sa mobilnim aplikacijama. U
IONIC možemo koristit CSS verziju liste ili direktive listu. Jadna od prednosti korišćenja
direktive liste je to što omogućava: ion-delete-button, ion-reorder-button, ion-optionbutton;
što pomaže pri boljem upravljanju sa listom. collection-repeat omogućava prikazivanje
dugačke liste.
Form Inputs (ion-checkbox, ion-radio i ion-toggle)
ion-checkbox se ponaša isto kao i AngularJS checkbox. ion-radio je isto što i HTML radio
input a ponaša se kao AngularJS radio. ion-toggle je dirktiva koja omogućava postavljanje
prekidača.
Gestures, Events (on-hold, on-tap, on-double-tap, on-touch, on-release on-
drag, on-swipe)
on-hold omogućava podešavanje događaja koj će se desiti kad je ekran pritisnu na
vremenski period od 500ms. on-tap omogućava podešavanje događaja koj će se desiti
kad je ekran pritisnu na vremenski period od 250ms. on-double-tap omogućava
podešavanje događaja koj će se desiti kad je ekran pritisnut dva puta. o n-touch ovaj gest
se poziva odmah čim korisnik pipne ekran. on-release ovaj gest se poziva odmah čim
korisnik prestane da dodiruje ekran. on-drag omogućava pomeranje jednim dodirom svuda
po stranici, on-drag-up izvršava se kad se element povuče na gore, on-drag-right izvršava
se kad se element povuče na desno, on-drag-down izvršava se kad se element povuče
na dole, on-drag-left izvršava se kad se element povuče na levo. on-swipe izvršava se
kada se ekran dodiruje velikom brzinom, on-swipe-up izvršava se kada se ekran dodiruje
velikom brzinom na gore, on-swipe-right izvršava se kada se ekran dodiruje velikom
brzinom u desno, on-swipe-down izvršava se kada se ekran dodiruje velikom brzinom na
dole, on-swipe-left izvršava se kada se ekran dodiruje velikom brzinom u levo.
Keyboard (keyboard-attach, hide-on-keyboard-open)
keyboard-attach je direktiva koja će izazvati pomeranje sadržaja na gore kada je tastatura
pozvana. hide-on-keyboard-open omogućava skrivanje elementa kada se tastatura pojavi.
Model (ionicModal)
ionicModal je instanca servisa $ionicModal. Model će pozivati funkcije poput modal.shown,
modal.hidden i modal.removed.
Scroll (ion-scroll, ion-infinite-scroll)
ion-scroll omogućava pomeranje sadržaja na celoj stranici. ion-infinite-scroll direktiva
omogućava pozivanje funkcije kad god korisnik dodirne dno ekrana.
Spinner (ion-spinner)
ion-spinner direktiva omogućava podešavanje različitih animacija prilikom učitavanja
aplikacije.
Neki od IONIC servisa su:
Platform ($ionicPlatform),
Scroll ($ionicScrollDelegate),
Modals ($ionicModal),
Navbar ($ionicNavBarDelegate),
History ($ionicHistory),
Popup ($ionicPopup),
Popover ($ionicPopover),
Action Sheet ($ionicActionSheet),
Backdrop ($ionicBackdrop),
Gesture ($ionicGesture),
Loading ($ionicLoading),
HTTP ($http)
LoadingConfig ($ionicLoadingConfig)
Tabs ($ionicTabsDelegate)
SlideBox ($ionicSlideBoxDelegate)
SideMenu ($ionicSideMenuDelegate)
Platform ($ionicPlatform)
Ovaj servis omogućava bolju kontrolu i ponašanje aplikacije. U okviru ovog servisa nalazi
se ready metoda koja se izvršava jednom kada je uređaj spreman. Svi kodovi koji su
povezani sa Cordova moraju biti napisaniu okviru $ionicPlatform.ready metode da bi se svi
plugin-ovi inicijalizovali i bili spremni za upotrebu odmah na početku. $ionicPlatform ima
praktičnu metodu za definisanje događaja koji se dešavaju nakon pritiska dugmeta za
povratak a to je onHardwareBackButtoni. $ionicPlatform takođe ima i
registerBackButtonAction metodu, ovo je jos jedna metoda koja omogućava kontrolu
načina na koji će se aplikacija ponašati nakon pritiska dugmeta za povratak.
$ionicPlatform ima i generičnu on metodu koja pmogućava neke funkcije kao što su
naprimer: pause, application resume, volumedownbutton, volumeupbutton.
Scroll ($ionicScrollDelegate)
ovaj servis omoguć API metode za kontrolu pomeranja sadržaja kao što su scroll i zoom.
Navbar ($ionicNavBarDelegate) ovaj servis služi za kontrolu ion-nav-bar.
History ($ionicHistory)
Vodi računa o koracima navigacije. $ionicHistory se može iskoristiti i za pojedinačne
metode kao što su: currentView, currentHistoryId, currentTitle, backView, backTitle,
forwardView, currentStateName. $ionicHistory servis ima još tri metode: goBack – vraća
jednu stranicu aplikacije nazat; clearHistory – briše istoriju; clearCache – ovo briše Cache
svih pregleda.
Popover ($ionicPopover)
$ionicPopover servis se uglavnom primenjuje na stavku koja se sledeća pojavljuje, koristi
se za prikazivanje tekstulnog sadržaja ili informacija.
Popup ($ionicPopup)
$ionicPopup servis omogućava kreiranje Popup sadržaja na koji korisnik mora da odgovori
da bi nastavio da radi sa aplikacijom. Ovi Popup-ovi su stilizovana verzija JavaScript
stanja alert, prompt i confirm methods.
Backdrop ($ionicBackdrop)
$ionicBackdrop servis omogućava prikazivanje i skrivanje pozadine preko UI.
Gesture ($ionicGesture)
$ionicGesture je Angular servis koj omogućava izvršenje ionic.EventController gesta.
7. IONIC aplikacija sliding-puzzle
Prilikom pokretanja aplikacije prvo se izvrašava ovaj kod koj se nalazi u www foderu
u fajlu pod nazivom index.html. Ovaj kod služi za pozivanje svih potrebnih sktipti koje će
se koristiti u aplikaciji, kao i izvršenje same aplikacije.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no,
width=device-width">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap:
https://ssl.gstatic.com; style-src 'self' 'unsafe-inline'; media-src *">
<title></title>
<link rel="stylesheet" href="lib/ionic/css/ionic.css" />
<link rel="stylesheet" type="text/css" href="css/layout.css" />
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="cordova.js"></script>
<!--
<script type="text/javascript" src="assets/js/angular.js"></script>
<script type="text/javascript" src="assets/js/angular-route.js"></script> -->
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/services.js"></script>
</head>
<body ng-app="puzzleApp">
<ion-nav-view>
</ion-nav-view>
</body>
</html>
Slika7: prva strana aplikacije
Ovaj deo koda služi za definisanje izgleda prve strane aplikacije i nalaz se u www
folderu u okviru foldera aplikacija pod nazivom index.html
<ion-view view-title="Sliding Puzzle">
<ion-header-bar class="bar-royal">
<h1 class="title">Sliding Puzzle</h1>
</ion-header-bar>
<ion-content>
<div class="list">
<a class="item item-icon-right" href="#/gallery">Igraj
<i class="icon ion-arrow-right-c"></i>
</a>
</div>
</ion-content>
</ion-view>
Slika8: izgled druge stranice aplikacije
Ovaj deo koda služi za definisanje izgleda druge strane aplikacije i nalaz se u www
folderu u okviru foldera aplikacija pod nazivom gallery.html
<ion-view view-title="Izaberi sliku">
<ion-header-bar class="bar-royal">
<h1 class="title">Izaberi sliku</h1>
</ion-header-bar>
<ion-content>
<div class="row" ng-repeat="levels in availableLevels">
<div class="col gallery-row" ng-repeat="level in levels">
<a href="#/puzzle/select/{{ level.puzzle.src }}" ng-if="level.canPlay">
<img ng-src="img/{{ level.puzzle.src }}" class="img-responsive" />
</a>
<a ng-if="!level.canPlay">
<div class="locked-layer">
<img class="locked img-responsive" ng-src="img/{{ level.puzzle.src }}" />
</div>
</a>
</div>
</div>
</ion-content>
</ion-view>
slika9: izgled treće strane aplikacije kada slika nije rešena ni jednom
slika10: izgled treće strane aplikacije kada je slika već rešena jednom
slika11: izgled treće strane aplikacije kada je telefon u drugom polažaju
Ovaj deo koda služi za definisanje izgleda trece strane aplikacije i nalaz se u www
folderu u okviru foldera aplikacija pod nazivom puzzle-select.html
<ion-view view-title="Izaberi tezinu">
<ion-header-bar class="bar-royal">
<h1 class="title">Izaberi tezinu</h1>
</ion-header-bar>
<ion-content>
<div class="row">
<img ng-src="{{ fullSrc }}" class="img-responsive center-block" style="max-height: 50vh"/>
</div>
<div class="row">
<div class="col col-50">
<a href="#{{ playableLevels.indexOf(3) >= 0 ? ('/puzzle/play/3/' + puzzle.src ) : '' }}"
class="button button-positive button-block play-btn" role="button">igraj 3x3</a>
</div>
<div class="col col-50">
<a href="#{{ playableLevels.indexOf(4) >= 0 ? ('/puzzle/play/4/' + puzzle.src ) : '' }}"
class="button button-positive button-block play-btn {{ playableLevels.indexOf(4) == -1 ?
'disabled' : '' }}" role="button">igraj 4x4</a>
</div>
</div>
</ion-content>
</ion-view>
slika12: izgled četvrte strane aplikacije kada se izabere težina 3x3
slika13: izgled četvrte strane aplikacije kada se izabere težina 4x4
Ovaj deo koda služi za definisanje izgleda četvrte strane aplikacije i nalaz se u www
folderu u okviru foldera aplikacija pod nazivom play.html
<ion-view view-title="Igraj">
<ion-header-bar class="bar-royal">
<h1 class="title">Igraj</h1>
</ion-header-bar>
<ion-content>
<div class="row" id="container" ng-init="matrixClass='matrix-cell-' + partitions">
<div class="col">
<div ng-show="originalShown" class="row matrix-row" ng-repeat-start="row in
originalSources">
<img ng-src="{{ originalSources[$parent.$index][$index] }}" ng-click="cellClick($parent.
$index, $index)" class="matrix-cell" ng-class="matrixClass" ng-repeat="col in row" />
</div>
<div ng-show="originalShown" class="clearfix" ng-repeat-end></div>
<div ng-show="containerShown" class="row matrix-row" ng-repeat-start="row in sources">
<img ng-src="{{ sources[$parent.$index][$index] }}" ng-click="cellClick($parent.$index,
$index)" class="matrix-cell" ng-class="matrixClass" ng-repeat="col in row" />
</div>
<div ng-show="containerShown" class=" clearfix" ng-repeat-end></div>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<h4>Potezi: {{ moves }}</h4>
</div>
<div class="col">
<h4>Vreme: {{ minutes }}:{{ seconds }}</h4>
</div>
<div class="col">
<button ng-hide="victory" class="button glyphicon ion-image"
ng-mousedown="showOriginal()" ng-mouseup="hideOriginal()"
on-touch="showOriginal()" on-release="hideOriginal()"></button>
<a href="#/gallery" ng-show="victory" class="button button-positive">Povratak u galeriju</a>
</div>
</div>
</div>
</ion-content>
</ion-view>
slika14: izled četvrte stranice aplikacije kad je slika rešena
Ovaj deo koda služi za definisanje izgleda četvrte strane aplikacije kada je slika
rešena i nalaz se u www folderu u okviru foldera aplikacija pod nazivom victory.html
<p>Uspeli ste u <span class="bold positive" ng-bind="moves"></span> poteza i <span
class="bold positive" >{{ minutes }}:{{ seconds }}</span></p>
U okviru www foldera nalazie se css folder u kome je smešten layout.css fajl koj
sadrži kod koj služi za stilizovanje aplikacije.
*{
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
body {
-webkit-touch-callout: none;
-webkit-text-size-adjust: none;
-webkit-user-select: none;
margin:0px;
padding:0px;
width:100%;
}
#container {
background-color: #ccc;
padding-left: 5vw;
padding-right:5vw;
}
#original {
max-height: 75vh;
}
.matrix-row {
padding: 0;
}
.matrix-cell {
float: right;
border: 1px solid #aaa;
}
.matrix-cell-3 {
max-height: 25vh;
max-width: 25vw;
}
.matrix-cell-4 {
max-height: 18.75vh;
max-width: 18.75vw;
}
.matrix-cell.selected {
border: 1px red solid;
}
.locked {
opacity: 0.5;
}
.locked-layer {
position:relative;
background:url('../img/lock.png') no-repeat center;
z-index:999;
display:block;
background-size: 50%
}
.gallery-row {
margin-bottom: 1%;
}
.play-btn {
margin: 1%;
}
.img-responsive {
max-width: 100%;
height: auto;
}
.center-block {
display: block;
margin-right: auto;
margin-left: auto;
}
U www folderu se takođe nalazi i js folder de su snešteni JavaScript fajlovi poput
app.js, controllers.js i services.js u kojima se definišu kontrolne funkcije, servisi i
ponašanje apilikacije.
app.js
'use strict';
angular.module('puzzleApp', ['ionic', 'puzzleControllers', 'puzzleServices'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
console.log("on $ionicPlatform.ready");
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'aplikacija/index.html',
controller: 'IndexCtrl'
})
.state('gallery', {
url: '/gallery',
cache: false,
templateUrl: 'aplikacija/gallery.html',
controller: 'GalleryCtrl'
})
.state('puzzleSelect', {
url: '/puzzle/select/:src',
cache: false,
templateUrl: 'aplikacija/puzzle-select.html',
controller: 'SelectCtrl'
})
.state('play', {
url: '/puzzle/play/:size/:src',
cache: false,
templateUrl: 'aplikacija/play.html',
controller: 'PlayCtrl'
});
$urlRouterProvider.otherwise('/index');
}]);
services.js
'use strict';
var puzzleServices = angular.module('puzzleServices', []);
puzzleServices.factory('PuzzleMatrix', function() {
return {
initialize: function(width, height) {
this.width = width || 3;
this.height = height || 3;
this.buildTiles();
},
buildTiles: function() {
this.tiles = new Array(this.width);
for(var i = 0; i < this.width; i++) {
this.tiles[i] = new Array(this.height);
for(var j = 0; j < this.height; j++) {
this.tiles[i][j] = { i: i, j: j};
}
}
this.tiles[this.width - 1][this.height - 1] = {i: null, j: null};
this.shuffle();
},
shuffle: function () {
var i = 0, iterations, increments, possiblePositions, nullI, nullJ, aux
increments = [{ i: -1, j: 0 }, { i: 1, j: 0 }, { i: 0, j: -1 }, { i: 0, j: 1 }];
nullI = this.width - 1;
nullJ = this.height - 1;
iterations = 15 + Math.floor(Math.random() * 20);
while (i < iterations) {
possiblePositions = [];
for (var k = increments.length - 1; k >= 0; k--) {
var increment = increments[k];
var i1 = nullI + increment.i, j1 = nullJ + increment.j;
if ((i1 >= 0 && i1 < this.width) &&
(j1 >= 0 && j1 < this.height)) {
possiblePositions.push({ i: i1, j: j1 });
}
}
var aux, auxNullI, auxNullJ;
aux = Math.floor(possiblePositions.length * Math.random());
auxNullI = possiblePositions[aux].i;
auxNullJ = possiblePositions[aux].j;
aux = this.tiles[auxNullI][auxNullJ];
this.tiles[auxNullI][auxNullJ] = { i: null, j: null };
this.tiles[nullI][nullJ] = aux;
nullI = auxNullI;
nullJ = auxNullJ;
i++;
}
},
isCompleted: function() {
var tile;
for(var i = 0; i < (this.width - 1); i++) {
for(var j = 0; j < this.height; j++) {
tile = this.tiles[i][j];
if (i !== tile.i || j !== tile.j) {
return false;
}
}
}
var i = this.width - 1;
for(var j = 0; j < this.height - 1; j++) {
tile = this.tiles[i][j];
if (i !== tile.i || j !== tile.j) {
return false;
}
}
return true;
}
};
});
puzzleServices.factory('AdService', function() {
var admobId = 'ca-app-pub-4620086008956912/4688100782',
analyticsId = 'UA-36061398-3';
if (document.URL.indexOf('http') === 0 || !window.AdMob) {
return {
showAdmob: function() {}, initAnalytics: function() {}
};
}
AdMob.setOptions({
isTesting: true
});
return {
showAdmob: function() {
AdMob.createBanner({
adId: admobId,
position: AdMob.AD_POSITION.BOTTOM_CENTER,
autoShow: true
});
},
initAnalytics: function() {
if (window.analytics && analytics) {
analytics.startTrackerWithId(analyticsId);
analytics.trackView('Home');
}
}
};
});
puzzleServices.factory('PuzzleManager', function() {
var puzzles = [
{id: "slika1", src: "1.jpg", order: 0},
{id: "slika2", src: "2.jpg", order: 1 },
{id: "slika3", src: "3.jpg", order: 2},
{ id: "slika4", src: "4.jpg", order: 3, },
{ id: "slika5", src: "5.jpg", order: 4 },
{ id: "slika6", src: "6.jpg", order: 5 },
{ id: "slika7", src: "7.jpg", order: 6 },
{ id: "slika8", src: "8.jpg", order: 7, },
];
return {
SESSION_KEY: 'puzzle.session',
getAll: function() {
return puzzles;
},
getSession: function() {
var session = localStorage.getItem(this.SESSION_KEY);
return JSON.parse(session || '{}');
},
setSession: function(session) {
localStorage.setItem(this.SESSION_KEY, JSON.stringify(session));
},
getSessionUnlocked: function() {
var session = this.getSession();
return session.unlocked || [];
},
isUnlocked: function(puzzle) {
if (puzzle.order < 2) {
return true;
}
var unlocked = this.getSessionUnlocked(),
maxOrder = -1;
angular.forEach(unlocked, function(value, key) {
maxOrder = Math.max(value.order, maxOrder);
});
return maxOrder >= puzzle.order - 1;
},
findBySrc: function(src) {
for(var i = puzzles.length - 1; i >= 0; i--) {
if (puzzles[i].src === src) {
return puzzles[i];
}
}
return null;
},
getPlayableLevels: function(puzzle) {
var levels = [3],
unlocked,
found;
unlocked = this.getSessionUnlocked();
for(var i = unlocked.length - 1; i >= 0; i--) {
if (puzzle.id == unlocked[i].id) {
levels.push(4);
break;
}
}
return levels;
},
onAchievedPuzzle: function(puzzle, size) {
var session = this.getSession(),
unlocked = session.unlocked || [],
puzzleCopy;
for(var i = unlocked.length - 1; i >= 0; i--) {
if (puzzle.id == unlocked[i].id) {
if (unlocked[i].sizes.indexOf(size) < 0) {
unlocked[i].sizes.push(size);
this.setSession(session);
}
return;
}
}
puzzleCopy = angular.copy(puzzle);
puzzleCopy.sizes = [size];
session.unlocked = unlocked;
session.unlocked.push(puzzleCopy);
this.setSession(session);
return;
}
};
});
puzzleServices.factory('ImageCropper', function () {
return {
initialize: function (width, height, image) {
this.width = width;
this.height = height;
this.image = image;
this._initializeCanvas();
this.cache = new Array(width);
for(var i = width - 1; i >= 0; i--) {
this.cache[i] = new Array(height);
for (var j = height - 1; j >= 0; j--) {
this.cache[i][j] = null;
}
}
},
_initializeCanvas: function () {
this.canvas = document.createElement('canvas');
this.canvas.setAttribute('width', this.width);
this.canvas.setAttribute('height', this.height);
this.context = this.canvas.getContext('2d');
},
crop: function (row, col) {
if (!this.image.complete) {
return null;
}
if (null !== this.cache[row][col]) {
return this.cache[row][col];
}
var r;
this.context.drawImage(this.image, col * this.width, row * this.height, this.width, this.height, 0, 0,
this.width, this.height);
r = this.canvas.toDataURL();
this.cache[row][col] = r;
return r;
}
}
});
puzzleServices.factory('PuzzleRenderer', ['PuzzleMatrix', 'ImageCropper', function (PuzzleMatrix,
ImageCropper) {
return {
initialize: function (partitions) {
this.partitions = partitions;
},
render: function (puzzle) {
var i, j, obj, x, src, sources = [];
for (i = 0; i < this.partitions; i++) {
x = [];
for (j = 0; j < this.partitions; j++) {
obj = PuzzleMatrix.tiles[i][j];
if (obj.i === null) {
src = "img/" + ImageCropper.width + "x" + ImageCropper.height + ".png";
} else {
src = ImageCropper.crop(obj.i, obj.j);
}
x.push(src);
}
sources.push(x);
}
return sources;
},
};
}]);
controllers.js
'use strict';
var IMAGES_SIZE = 1200;
var puzzleControllers = angular.module('puzzleControllers', []);
puzzleControllers.controller('IndexCtrl', ['$scope', function ($scope) {
}]);
puzzleControllers.controller('GalleryCtrl', ['$scope', 'AdService', 'PuzzleManager', function($scope,
AdService, PuzzleManager) {
var availableLevels = [], lastArray = null;
angular.forEach(PuzzleManager.getAll(), function (puzzle, index) {
if (index % 2 == 0) {
lastArray = [];
availableLevels.push(lastArray);
}
lastArray.push({
puzzle: puzzle,
canPlay: PuzzleManager.isUnlocked(puzzle)
});
});
$scope.availableLevels = availableLevels;
//AdService.showAdmob();
}]);
puzzleControllers.controller('SelectCtrl', ['$scope', '$stateParams', 'PuzzleManager', function($scope,
$stateParams, PuzzleManager) {
var puzzle = PuzzleManager.findBySrc($stateParams.src);
$scope.fullSrc = 'img/' + $stateParams.src;
$scope.puzzle = puzzle;
$scope.playableLevels = PuzzleManager.getPlayableLevels(puzzle);
}]);
puzzleControllers.controller('PlayCtrl', ['$stateParams', '$scope', 'PuzzleMatrix', 'PuzzleManager',
'ImageCropper', 'PuzzleRenderer', '$ionicLoading', '$ionicPopup', '$interval', '$timeout',
function ($stateParams, $scope, PuzzleMatrix, PuzzleManager, ImageCropper, PuzzleRenderer,
$ionicLoading, $ionicPopup, $interval, $timeout) {
$ionicLoading.show({
template: "Loading..."
});
var onChange, partitions, initTime, timeInterval;
partitions = parseInt($stateParams.size);
$scope.moves = 0;
$scope.victory = false;
$scope.imgSource = 'img/' + $stateParams.src;
$scope.containerShown = true;
$scope.originalShown = false;
$scope.puzzleMatrix = PuzzleMatrix;
$scope.partitions = partitions;
$scope.showOriginal = function() {
$scope.originalShown = true;
$scope.containerShown = false;
};
$scope.hideOriginal = function() {
$scope.containerShown = true;
$scope.originalShown = false;
};
$scope.cellClick = function (i, j) {
var tile, possibleTiles;
tile = PuzzleMatrix.tiles[i][j];
if (tile.i !== null) {
$scope.moves++;
possibleTiles = [{ i: i - 1, j: j }, { i: i + 1, j: j }, { i: i, j: j - 1 }, { i: i, j: j + 1 }];
for (var k = 0; k < possibleTiles.length; k++) {
var possibleTile = possibleTiles[k];
var i1 = possibleTile.i, j1 = possibleTile.j;
if (i1 < 0 || i1 >= partitions ||
j1 < 0 || j1 >= partitions) {
continue;
}
if (PuzzleMatrix.tiles[i1][j1].i === null && PuzzleMatrix.tiles[i1][j1].j === null) {
PuzzleMatrix.tiles[i][j] = PuzzleMatrix.tiles[i1][j1];
PuzzleMatrix.tiles[i1][j1] = tile;
onChange();
break;
}
}
}
};
onChange = function () {
//console.log("on change triggered");
$scope.sources = PuzzleRenderer.render(PuzzleMatrix);
if (PuzzleMatrix.isCompleted()) {
$scope.victory = true;
}
};
$scope.$watch('victory', function () {
if (!$scope.victory) {
return;
}
$interval.cancel(timeInterval);
PuzzleManager.onAchievedPuzzle(PuzzleManager.findBySrc($stateParams.src), partitions);
$scope.sources[partitions - 1][partitions - 1] = ImageCropper.crop(partitions - 1, partitions - 1);
$timeout(function () {
$ionicPopup.alert({
templateUrl: 'aplikacija/victory.html',
title: "BRAVO",
scope: $scope
});
}, 500);
});
var img = new Image();
img.onload = function () {
ImageCropper.initialize(IMAGES_SIZE / partitions, IMAGES_SIZE / partitions, img);
PuzzleMatrix.initialize(partitions, partitions);
PuzzleRenderer.initialize(partitions);
$scope.sources = PuzzleRenderer.render(PuzzleMatrix);
$scope.originalSources = new Array(partitions);
for (var row = partitions - 1; row >= 0; row--) {
$scope.originalSources[row] = new Array(partitions);
for (var col = partitions - 1; col >= 0; col--) {
$scope.originalSources[row][col] = ImageCropper.crop(row, col);
}
}
$ionicLoading.hide();
initTime = Math.floor((new Date()).getTime() / 1000);
timeInterval = $interval(function () {
var now, elapsedTime, seconds;
now = Math.floor((new Date()).getTime() / 1000);
elapsedTime = now - initTime;
seconds = elapsedTime % 60;
if (seconds < 10) {
seconds = "0" + seconds.toString();
} else {
seconds = seconds.toString();
}
$scope.seconds = seconds;
$scope.minutes = Math.floor(elapsedTime / 60);
}, 1000);
};
img.src = $scope.imgSource;
if (img.complete) {
img.onload();
console.log("Slika resena iz prvog pokusaja" + img.src);
} else {
console.log("Slika nije resena iz prvog pokusaja" + img.src);
}
}]);
gulpfile.js
var gulp = require('gulp');
var gutil = require('gulp-util');
var bower = require('bower');
var concat = require('gulp-concat');
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');
var sh = require('shelljs');
var paths = {
sass: ['./scss/**/*.scss']
};
gulp.task('default', ['sass']);
gulp.task('sass', function(done) {
gulp.src('./scss/ionic.app.scss')
.pipe(sass({
errLogToConsole: true
}))
.pipe(gulp.dest('./www/css/'))
.pipe(minifyCss({
keepSpecialComments: 0
}))
.pipe(rename({ extname: '.min.css' }))
.pipe(gulp.dest('./www/css/'))
.on('end', done);
});
gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']);
});
gulp.task('install', ['git-check'], function() {
return bower.commands.install()
.on('log', function(data) {
gutil.log('bower', gutil.colors.cyan(data.id), data.message);
});
});
gulp.task('git-check', function(done) {
if (!sh.which('git')) {
console.log(
' ' + gutil.colors.red('Git is not installed.'),
'\n Git, the version control system, is required to download Ionic.',
'\n Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
'\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
);
process.exit(1);
}
done();
});
hooks folder 010_add_platform_class.js
#!/usr/bin/env node
// Add Platform Class
// Automatically adds the platform class to the body tag
// after the `prepare` command. By placing the platform CSS classes
// directly in the HTML built for the platform, it speeds up
// rendering the correct layout/style for the specific platform
// instead of waiting for the JS to figure out the correct classes.
var fs = require('fs');
var path = require('path');
var rootdir = process.argv[2];
function addPlatformBodyTag(indexPath, platform) {
// add the platform class to the body tag
try {
var platformClass = 'platform-' + platform;
var cordovaClass = 'platform-cordova platform-webview';
var html = fs.readFileSync(indexPath, 'utf8');
var bodyTag = findBodyTag(html);
if(!bodyTag) return; // no opening body tag, something's wrong
if(bodyTag.indexOf(platformClass) > -1) return; // already added
var newBodyTag = bodyTag;
var classAttr = findClassAttr(bodyTag);
if(classAttr) {
// body tag has existing class attribute, add the classname
var endingQuote = classAttr.substring(classAttr.length-1);
var newClassAttr = classAttr.substring(0, classAttr.length-1);
newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote;
newBodyTag = bodyTag.replace(classAttr, newClassAttr);
} else {
// add class attribute to the body tag
newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">');
}
html = html.replace(bodyTag, newBodyTag);
fs.writeFileSync(indexPath, html, 'utf8');
process.stdout.write('add to body class: ' + platformClass + '\n');
} catch(e) {
process.stdout.write(e);
}
}
function findBodyTag(html) {
// get the body tag
try{
return html.match(/<body(?=[\s>])(.*?)>/gi)[0];
}catch(e){}
} ?
function findClassAttr(bodyTag) {
// get the body tag's class attribute
try{
return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0];
}catch(e){}
}
if (rootdir) {
// go through each of the platform directories that have been prepared
var platforms = (process.env.CORDOVA_PLATFORMS
process.env.CORDOVA_PLATFORMS.split(',') : []);
for(var x=0; x<platforms.length; x++) {
// open up the index.html file at the www root
try {
var platform = platforms[x].trim().toLowerCase();
var indexPath;
if(platform == 'android') {
indexPath = path.join('platforms', platform, 'assets', 'www', 'index.html');
} else {
indexPath = path.join('platforms', platform, 'www', 'index.html');
}
if(fs.existsSync(indexPath)) {
addPlatformBodyTag(indexPath, platform);
}
} catch(e) {
process.stdout.write(e);
}
}
}
8. Zaključak
Razvoj aplikacija na ovaj na način omogućava da se aplikacija razvije za relativno
kratko vreme upotrebom standardizovanih tehnologija, pri čemu se isti izvorni kod može
upotrebiti za pakovanje i distribuciju aplikacije na više različitih platformi. Osim toga, ovako
dobijen izvorni kod aplikacije je jednostavan za održavanje i prilagođavanje.
Aplikacije razvijene pomoću IONIC mogu koristiti veliki broj native funkcionalnosti
uređaja. Upotrebom HTML5 i CSS3, moguće je napraviti potpuno funkcionalne aplikacije
sa „bogatim“ grafičkim interfejsom, pri čemu je proces dizajniranja aplikacija veoma
jednostavan. Pri dizajniranju je moguće koristiti programe za grafičku izradu HTML
stranica. Bitno je napomenuti i da IONIC koristi Cordova API koj je veoma dobro
dokumentovan, pri čemu su navedene preporuke i ograničenja u korištenju svake
funkcionalnosti za svaku podržanu platformu.
Ipak, proces razvoja i distribucije aplikacija na različite platforme, kao i korištenja
funkcionalnosti samih platformi nije tako idealan kako se čini. Aplikacije koje se razvijaju za
izvršavanje na više platformi često izgledaju isto na svim platformama što znači da nisu
„domaće“ ni na jednoj platformi, pa je potreban dodatan rad programera kako bi se
aplikacija vizuelno prilagodila određenoj platformi. Zbog činjenice da veliki broj HTML5
funkcionalnosti nije podržan na svim platformama, nije moguće u potpunosti iskoristiti sve
mogućnosti za razvoj, a opcije koje su iskorištene u aplikaciji namenjenoj za jednu
platformu ne moraju biti podržane na nekoj drugoj platformi.
Za aplikacije kod kojih su veoma važne performanse izvršavanja, preporučuje se
native način razvoja. Na kraju, bitno je još jednom naglasiti da svaki od načina razvoja
mobilnih aplikacija ima određene prednosti i nedostatke, te da ne postoji najbolji način
razvoja. Isto tako, ukoliko se nastavi sa ubrzanim razvojem tehnologija koje se koriste pri
razvoju hibridnih aplikacija, ovaj način razvoja mobilnih aplikacija bi mogao postati
dominantan.
slika15: Zaključak