Szakdolgozat
Miskolci Egyetem
Étkezési napló alkalmazás fejlesztése Android platformra
Készítette: Sütő Ádám Programtervező Informatikus Témavezető: Elek Tibor
Miskolc, 2016
Miskolci Egyetem Gépészmérnöki és Informatikai Kar Alkalmazott Matematikai Tanszék
Szám:
Szakdolgozat Feladat Sütő Ádám (FCA194) programtervező informatikus jelölt részére. A szakdolgozat tárgyköre: szoftverfejlesztés A szakdolgozat címe: Étkezési napló alkalmazás fejlesztése Android platformra A feladat részletezése: Androidos applikáció fejlesztése, amely a következő feladatokat látja el: • adatokat tárol a felhasználó napi étrendjéről • adatbázisba rendezve tárol előre beállított ételeket, melyeket a felhasználó is bővíthet • megtekinthetőek a napi bevitt tápértéktartalmak, anyagokra bontva • a felhasználó megtekintheti az egyes ételek tápértékeit, illetve a bennük levő összetevőket
Témavezető(k): Elek Tibor, mérnöktanár Konzulens(ek): A feladat kiadásának ideje: 2015. szeptember 23.
................................. szakfelelős 2
Eredetiségi Nyilatkozat
Alulírott Sütő Ádám; Neptun-kód: FCA194 a Miskolci Egyetem Gépészmérnöki és Informatikai Karának végzős Programtervező Informatikus szakos hallgatója ezennel büntetőjogi és fegyelmi felelősségem tudatában nyilatkozom és aláírásommal igazolom, hogy Étkezési napló alkalmazás fejlesztése Android platformra című szakdolgozatom/diplomatervem saját, önálló munkám; az abban hivatkozott szakirodalom felhasználása a forráskezelés szabályai szerint történt. Tudomásul veszem, hogy szakdolgozat esetén plágiumnak számít: • szószerinti idézet közlése idézőjel és hivatkozás megjelölése nélkül; • tartalmi idézet hivatkozás megjelölése nélkül; • más publikált gondolatainak saját gondolatként való feltüntetése. Alulírott kijelentem, hogy a plágium fogalmát megismertem, és tudomásul veszem, hogy plágium esetén szakdolgozatom visszautasításra kerül.
Miskolc, 2016.02.16.
................................. Hallgató
3
1. szükséges (módosítás külön lapon) A szakdolgozat feladat módosítása nem szükséges ......................
...........................
dátum
témavezető(k)
2. A feladat kidolgozását ellenőriztem: témavezető (dátum, aláírás):
konzulens (dátum, aláírás):
..............
.............
..............
.............
..............
.............
3. A szakdolgozat beadható: ......................
...........................
dátum
témavezető(k)
4. A szakdolgozat . . . . . . . . . . . . . . . . . . . szövegoldalt . . . . . . . . . . . . . . . . . . . program protokollt (listát, felhasználói leírást) . . . . . . . . . . . . . . . . . . . elektronikus adathordozót (részletezve) ................... . . . . . . . . . . . . . . . . . . . egyéb mellékletet (részletezve) ................... tartalmaz. ......................
...........................
dátum
témavezető(k)
5. bocsátható A szakdolgozat bírálatra nem bocsátható A bíráló neve: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......................
...........................
dátum
szakfelelős
6. A szakdolgozat osztályzata a témavezető javaslata:
................
a bíráló javaslata:
................
a szakdolgozat végleges eredménye: . . . . . . . . . . . . . . . . Miskolc, . . . . . . . . . . . . . . . . . . . . . . . .
................................. a Záróvizsga Bizottság Elnöke 4
Tartalomjegyzék 1. Bevezetés 2. Android platformról bővebben 2.1. Az Android fejlődésének fontosabb állomásai 2.2. Fejlesztői eszközök . . . . . . . . . . . . . . 2.2.1. Android Studio . . . . . . . . . . . . 2.2.2. Android SDK . . . . . . . . . . . . . 2.2.3. Gradle Scriptek . . . . . . . . . . . . 2.3. Androidos fejlesztés fogalmainak ismertetése 2.3.1. Activity . . . . . . . . . . . . . . . . 2.3.2. Fragmentek . . . . . . . . . . . . . . 2.3.3. Intentek és Intent filterek . . . . . . . 2.3.4. Erőforrások . . . . . . . . . . . . . . 2.3.5. A manifest fájl . . . . . . . . . . . . 2.3.6. Felhasználói felület . . . . . . . . . . 2.3.7. Elrendezések . . . . . . . . . . . . . .
6
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
7 9 10 10 11 12 12 12 14 15 16 18 18 19
3. Az alkalmazás fejlesztésének lépései 3.1. A tervezés fontos lépései . . . . . . . . . . . . . . . . . . . . . . 3.2. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3. Tervezett funkciók bemutatása . . . . . . . . . . . . . . . . . . . 3.3.1. Adatok tárolása . . . . . . . . . . . . . . . . . . . . . . . 3.4. Osztályok bemutatása . . . . . . . . . . . . . . . . . . . . . . . 3.4.1. Felhasználói felületet megvalósító osztályok . . . . . . . . 3.4.2. Adattárolás és egyéb funkciók osztályainak megvalósítása 3.5. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6. Felhasználói felületek . . . . . . . . . . . . . . . . . . . . . . . . 3.6.1. Főképernyő . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.2. Menü . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.3. Keresés . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.4. Táplálék hozzáadó . . . . . . . . . . . . . . . . . . . . . 3.6.5. Arhívum . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.7. Továbbfejlesztési tervek . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
23 23 23 23 25 25 26 34 36 38 38 39 40 41 42 42
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
4. Összefoglalás
44
Irodalomjegyzék
45
Adathordozó használati útmutató
46 5
1. fejezet Bevezetés Biztosan mindenkivel előfordult már, hogy egészségi állapotából kifolyólag rövid, vagy hosszú ideig úgy kellett táplálkoznia, hogy odafigyel mely anyagokból mennyit visz be szervezetébe. Alkalmazásom fő motivációja az előbb említett mondaton alapszik. Legfontosabb feladata, hogy ha bárhol - akár otthon, akár étteremben- szeretnénk tudni, hogy amit eszünk az milyen összetevőkből áll össze könnyedén rákereshessünk, illetve megjegyezhessük hogy az elmúlt napokban miből mennyit ettünk. Ez megkönnyíti életünket, ha valamilyen alapanyagot kerülni szeretnénk, fogyókúrázás, vagy egészségi állapottól függően. Ezen feladat kivitelezéséhez az okostelefon a legmegfelelőbb eszköz, lévén ez mindenkinél minden pillanatban ott van és könnyű a használata. Az eszközök piaca egyre jobban fellendülőben van, így a tervezés és kivitelezéskor megszerzett tudást később többszörösen is kamatoztathatom. Az alkalmazásom Androidos platformra épül, így a világon sok emberhez juthat el a későbbiekben. A világ egyik legnagyobb mobilplatformja az Android mely a Google gondozásában fejlődik. Fejlesztése abból a célból történt, hogy legyen egy egységes operációs rendszere az okostelefonoknak. Ezen célt a fejezet első mondatából láthatólag sikerült elérnie a platformnak. Piackutatások szerint a 2015-ös évben az ilyen rendszert használó telefonok mennyisége elérte az 1.4 billiót világszerte. A következő fejezetben az alkalmazás elkészítéséhez szükséges elengedhetetlen ismereteket írom le. Megismerkedünk milyen egységekből tevődik össze egy Androidos alkalmazás. Az ebben a fejezetben megszerzett ismeret segítségével építem fel a későbbiekben bemutatásra kerülő alkalmazásomat is. Szót ejtünk az Android platform saját fejlesztői környezetéről az Android Studióról, illetve az Android SDK-ról mely segítéségével a fejlesztés zajlik. Szakdolgozatom harmadik fejezetében az elkészült alkalmazásról és az elkészítés folyamatáról lesz szó. Elsőként leírom az alkalmazás tervezett funkcióit, fogalmait. Ezután ismertetem a tervezés során kialakult osztálystruktúrát, egyes részeit részletesebben. A fejezet végén ábrák segítségével illusztrálom a kész alkalmazás felhasználói felületét, illetve annak kezelését. Szót ejtek az esetleges továbbfejlesztési lehetőségekről is.
6
2. fejezet Android platformról bővebben
Az Android egy érintőkijelzős telefonokra szánt és optimalizált Linux kernel alapú operációs rendszer, vagy csak röviden OS. Érintőkijelző lévén a felhasználó és a rendszer közötti kommunikáció nagyban különbözik olyan OS-ekhez képest ahol ez a kapocs csak gombok és mutatók segítségével valósítható meg. Felhasználói felülete a különböző érintés-gesztusokon alapszik, mint például az oldalra húzás, egyszerű érintés vagy összecsípés. Az Android rendszer a Google fejlesztése, mely szerte a világban sok eszközön megtalálható mint például a Google TV, Android Auto vagy Android Wear eszközökön. Ezen eszközökön a kezelő felületek illetve egyéb funkcionalitások is eltérhetnek az Androidos telefon vagy tabletek Androidjaihoz képest. Ezen eszközök neveiből látható, hogy ez az OS széles körben elterjedt nem csak telefon és tableteken, de TV-ken (Google TV), autókban (Android Auto), illetve okoskarórákban és a Google saját fejlesztésű szemüvegében is megtalálhatóak. Az elkövetkező pár bekezdésben összehasonlítom a három legnagyobb hordozható eszközökre tervezett platformokat. Egy 2015-ös piackutatás szerint az Androidos telefonok részesedése 82.4%-os mely az évek során folyamatosan növekedik. A platform sajátossága, hogy nyílt forráskódú Unix alapokon nyugszik, míg a felhasználói megjelenítésről és az okostelefonokhoz kapcsolódó eszközök mint a WiFi, Bluetooth, Modem és érintőképernyő kezeléséről a rá épített Android keretrendszer gondoskodik. Legnagyobb előnye a fejezet első mondatából adódik, vagyis ezáltal a legtöbb felhasználója van mindegyik platform közül,így sok alkalmazás érhető el. Második legnagyobb előnye az, hogy nagyon sok gyártó telefonján található Android operációs rendszer mint például a Samsung, LG, Huawei. Ez azzal jár, hogy a telefonok gyártói között folyamatos versengés van, hogy melyikük tud legjobb hardwerrel szerelt és leggazdaságosabb anyagokból összeszerelt telefonokat előállítani. Ez a versengés azt eredményezi hogy minden árkategóriában találhatunk nekünk megfelelő telefont, illetve az ár és érték arány is itt a legmagasabb. Legnagyobb hátránya a platformnak, hogy mivel nagyon sok gyártó gyárthat ilyen telefonokat, így a hardwerek is nagyban különbözhetnek ezáltal sebességbeli és stabilitásbeli oldalon nem 7
az Android a legkiemelkedőbb. A fejlesztése a Google által történik, az alkalmazásokat Google Play áruházuk segítségével tölthetjük le. Második legnagyobb piaci részesedéssel rendelkező platform az Apple által fejlesztett iOS. Ezen platform sajátossága hogy csak az Apple által gyártott telefonokra kerülhet ilyen operációs rendszer, ezáltal a sebesség és stabilitása a rendszernek itt lehet a legjobb, lévén az operációs rendszernek egy hardwerrel kell tudnia együttműködni. Ezen platformon az alkalmazások fejlesztésére és minőség ellenőrzésére is nagyobb hangsújt fektet az Apple. Ezen platformra való fejlesztéshez elengedhetetlen egy Apple eszköz. Utolsó általam megemlített platform a Microsoft által fejlesztett Windows Phone. Ezen rendszer filozófiája, hogy egységes megjelenést nyújtson a felhasználónak a Windows PC-s és telefonon futtatott változata között. A platform története 2010-re nyúlik vissza mikor a Microsoft megvette a Nokiát. Felhasználói bázisa ezen platformnak a legkisebb, ezáltal a megtalálható alkalmazások mennyisége és minősége is a legalacsonyabb. Ezúton téjünk vissza szakdologozatom fő témájához az Androidhoz. Eredetileg az Android vállalat által kezdődött meg a fejlesztése, melyet a Google megvett 2005-ben. Első verziója 2008-ban jelent meg. A Google Play webáruháza napjainkban több mint másfél millió ingyenes és fizetős alkalmazást tartalmaz, ezzel a legnagyobb webáruház a konkurenciákhoz képest mint például az Apple App Store-ja vagy a Windows Phone Store. 2014-ben a Google napvilágra hozta, hogy több mint egy billió aktív Android felhasználó van havonta. A felhasználóknak az érintőkijelzőn keresztüli interakción kívül lehetőségük van különböző játék-kontrollerek, illetve fizikai billentyűzetek használatára is USB-n vagy Bluetooth-on keresztül csatlakoztatva. A felhasználói interakciók során a rendszer visszajelzést tud küldeni a felhasználónak az eszközökbe szerelt rezgőmotor segítségével. Az OS különböző gyorsulásmérők, illetve közelségszenzorok segítségével kapcsolatot tarthat a külvilággal, reagálhat a fényváltozásra vagy az eszköz megmozdítására. A rendszer elindulásakor a kezdőképernyőn találjuk magunkat, amelyek ikonokat és különböző widgeteket tartalmaznak. Ikonok segítségével alkalmazásokat indíthatunk a widgetek pedig élő, dinamikus információkat tartalmazhatnak, mint például az időjárást vagy az időt. Ezen widgetek lehetnek önálló alkalmazások is egyben amelyek csak a kezdőképernyőkön jelennek meg vagy lehetnek egy másik applikációhoz tartozó widgetek mint például egy buszmenetrendet tartalmazó applikáció amely kiírja egy busz következő indulási idejét widgetje segítségével a főoldalon. Ezekből a kezdőoldalakból többet is létrehozhat a felhasználó, illetve nagy mértékben testreszabható a kinézetük és elrendezésük az ikonoknak. Az applikációk az Android SDK (Software Development Kit) segítégével hozhatóak létre Java kódban. Az Android elsődleges fejlesztői környezete 2014 decembere óta az Android Studio, amely az IntelliJ IDEA környezeten alapul. A felhasználók alkalmazásokat a beépített bolton keresztül tölthetnek le, illetve külön letölthetőek az alkalmazások apk fájljai is melyek hasonlóak egy Windows-os környezetben levő telepítőfájlra. A beépített alkalmazás bolt legfőbb előnye hogy az onnan letöltött és telepített applikációkat a bolt automatikusan frissíti, illetve ellenőrzi a kiválasztott applikáció kompatibilitását a készülékkel illetve az OS verziójával. Mivel az Androidos eszközök akkumulátorról üzemelnek így a rendszer fogyasztását a lehető legkissebb szinten kell tartani. Ez úgy van biztosítva, hogy az olyan applikációk melyek nincsenek jelenleg használva azoknak működését a rendszer felfüggeszti így azok nem fogyasztanak CPU erőforrást, ezáltal nem csökkentik az eszköz üzemidejét. Ha a rendszer memóriája ki8
2.1. Az Android fejlődésének fontosabb állomásai fogyóban van, akkor a rendszer leállítja a legrégebben felfüggesztett alkalmazásokat. A legfőbb hardware közeg az Android számára az ARM arhitektúra. Ez az arhitektúra egy limitáltabb utasításkészlettel rendelkezik a többi arhitektúrához képest. A memória igénye egy 5.1-es verziójú viszonylag újnak mondható rendszernek 512 megabájttól egészen 2 gigabájtig mozoghat. A memóriaigény nagyban függ a kijelző felbontásának méretétől. Támogatott még az OpenGL ES is mely különböző 3D-s alkalmazásokban mint játékok játszik szerepet.
2.1. Az Android fejlődésének fontosabb állomásai Az 1.0 verziójú Android volt a legelső Android amely 2008-ban jelent meg. Ez a verzió legelőször a HTC Dream készülékeken jelent meg. Megjelent az Android Market, teljes méretű HTML megjelenítésére képes böngésző nagyítási lehetőségekkel. Kamerakezelés is volt, bár a legújabb verziókhoz képesti jóval szerényebb beállítási lehetőségekkel. Megjelent a YouTube lejátszó illetve a WiFi és Bluetooth támogatás is. Következő nagy ugrás az 1.6-os verzió melynek már fantázianeve is volt: Donut. Legnagyobb újítása a gyorskeresés volt mely lehetővé tette, hogy egyszerre kereshettünk vele az interneten illetve magán a készülék memóriáján is. Elérhetővé vált a 320x480 felbontásutól eltérő méretű kijelzők kezelése is, illetve átdolgozásra került az akkoriban még Android Market néven futó alkalmazásbolt is. Az Eclair nevű 2.1 verziójú Android-ban debütált a napjainkban egyik legismertebb navigációs szolgáltatás a Google Maps. A kezdőképernyő testreszabhatósága is megjelent, illetve hangfelismerő alkalmazása segítségével már beszédből szövegre is képes volt konvertálni az információt. A Froyo 2.2-es verzió továbbfejlesztette a hangfelismerést, ezáltal már különböző parancsokat is kiadhattunk telefonunknak mint például hogy váltson zenehallgatásra. WiFi Hotspot támogatás is belekerült. Bevezetésre került egy újfajta fordító is amely megötszörözte a CPU intenzív kódok futásának sebességét. A Gingerbread 2.3-as verzióban a játéké a hangsúly, az új API segítségével a fejlesztőknek még egyszerűbb lett 3D-s alkalmazásokat létrehozni. Támogatást nyert az NFC technológia is melynek lényege, hogy kis mennyiségű adatot tudunk segítségével két eszköz között átmozgatni, melyet e két eszköz összeérintésével érhetünk el. Ebben a verzióban jelent meg először az akkumulátor menedzselő felület melyben megtekinthettük, hogy a telefon mely hardveres részei vagy mely alkalmazások fogyasztották a legtöbb akkumulátor időt. A 3.0-s Honeycomb verzió csak táblagépekre jelent meg, ezáltal továbbfejlődött a táblagépes megjelenítés. Ebben a verzióban tűnt fel először a szoftveres irányítógombok mint a kezdőképernyő, vissza, illetve keresés gomb támogatás. A gyors beállítás segítségével egyetlen érintéssel elérhetővé váltak fontosabb beállítási és információs lehetőségek mint az idő, akkumulátor és internetes kapcsolat állapota. A 4.0-s Ice Cream Sandwich feladata a főképernyő továbbfejlesztése volt. Bekerültek az alkalmazás mappák, illetve kedvenceknek is lehetett jelölni az alkalmazásokat. Második legnagyobb és leghasznosabb újítása a mobil-internet nyomon követésének fejlesztése volt. A felhasználó beállíthatott havi maximum adatforgalmat, elkerülve így az esetleges túlfogyasztásból adódó költségeket. További NFC funkciók kerültek be mint hogy videók, képek és alkalmazások megosztása más eszközökkel. A 4.1-es verziójú Jelly Bean legfőbb újítása a Google Now, mely egy személyi aszisztensként funkciónál, információkat közöl az időjárásról, fontos találkozókról. Az érte9
2.2. Fejlesztői eszközök sítések sokkal több információval kerülnek kijelzésre, illetve a rájuk kattintással az értesítés típusától függően válaszolhatunk rájuk. Elérhetővé vált több Google Fiók használata egy eszközön. A KitKat 4.4-es verzió újdonságai közé tartozik az Ok Google hangutasítás amelynek segítségével utasíthatjuk telefonunkat anélkül hogy egyéb interakcióba lépnénk eszközünkkel. Átdolgozásra került az Android dizánjnja is és bekerült az okos tárcsázó, amely fontossági sorrendbe szervezi a legfontosabb személyeket a telefonkönyvünkben. Az 5.0-s Lollipop bevezeti a Material Design-t amely lehetővé teszi a fejlesztőknek a még egységesebb kinézetű alkalmazások létrehozását. Új funkció a többképernyős használat is mely segítségével könnyen folytathatjuk munkánkat egyik eszközről a másikra való áttérése során. Az értesítések továbbfejlesztésére is sor került, ezáltal már a feloldó képernyőn is megtekinthetjük azokat. A jelenleg futó legújabb verzió a 6.0 Marshmallow melynek újdonsága hogy a kezdőképernyő gombját hosszan megnyomva beolvassa a képernyőn megtalálható szöveget és az alapján segítséget nyújt a felhasználónak. Fontos új funkció a jogosultságok kezelése, mint például, hogy engedélyezzük-e egy applikációnak a kamera, mikrofon használatát, illetve a telefonkönyvben való olvasást. Továbbfejlesztésre került még a rendszer akkumulátor üzemidejének hosszabbítása is.
2.2. Fejlesztői eszközök 2.2.1. Android Studio Az Android Studio egy teljesen ingyenes IntelliJ IDEA-n alapuló fejlesztő eszköz. Az első stabil verzió 2014 decemberében jelent meg. A Google megjelenése óta ezt az eszközt tartja elsődleges Androidos fejlesztőeszköznek, így a megjelenése előtt széles körben elterjedt Eclipse-s Androidos plugin támogatása is egyre jobban a háttérbe szorult. Alapértelmezetten az Android Studio a projektünket Android projekt nézetben mutatja. Ezen nézet legfőbb tulajdonságai, hogy a fordításhoz szükséges fájlokat modulonként egy közös mappába helyezi, ugyan így az összes manifest fájlt is modulonként közös mappába rakja, illetve a különböző erőforrásfájlokat is egy mappába rakja függetlenül az erőforrás fájl lokalizáltságától, vagy kijelző-méretbeli tulajdonságától. Lokalizáltságon a megjelenő különböző nyelveket értjük, míg kijelző-méretbeli tulajdonságon, azt hogy a különböző méretű kijelzőkön mely felbontású képek jelennek meg. Négy fő mappát említenék meg melyeknek a feladatai a következőek: • java/ - ezen mappában találhatóak meg az alkalmazás java nyelvű forrásai • manifests/ - az úgynevezett manifest fájlok helye, későbbiekben lesz róla szó • res/ - külső erőforrások fájljai, mint például képek, hangok, illetve a megjelenésért felelős xml-ek • Gradle Scripts/ - a különböző Gradle scriptfájlok helye Lehetőségünk van monitorozni a cpu, illetve memóra használatot, illetve tudunk még debugolni is, van lehetőségünk létrehozni virtuális eszközöket az emulátor segítségével, ezekről bővebben a következő fejezetekben ejtek szót. 10
2.2. Fejlesztői eszközök Új projekt létrehozásakor elsősorban be kell írni a projektünk nevét, illetve egy webes elérési utat. Ezek után a projektünk classpath-a a projekt névből és az elérési útból fog összetevődni. Fontos, hogy Androidos rendszeren belül a különböző alkalmazások nevei a rendszer számára a classpath lesz. Egy egyszerű példa: a projekt nevem Étkezési napló, elérési út szakdolgozat.hu, így az alkalmazás classpath-a a következő lesz: hu.szakdolgozat.etkezesinaplo. Ezek után kiválasztjuk azt az eszközt amelyre szeretnénk az alkalmazásunkat létrehozni, mint például telefon, tablet, TV, vagy Google Glass. Ezek után eszköztől függően tudjuk kiválasztani a minimum támogatott Android verziót. Itt fontolóra kell vennünk, hogy ha kisebb verziójú Androidra szeretnénk írni, akkor az eszközök száma amelyen az alkalmazásunk futni tud nagyobb, viszont az alacsonyabb verzió kevesebb lehetőséget, funkciót biztosít. Ha viszont nagyobb verziót választunk ezáltal kevesebb eszközön fog tudni futni, viszont a funkciók tárháza nagyobb lesz. Következő lépésben úgynevezett activity, vagy aktivitást kell létrehoznunk. Az activity az alkalmazás egy teljes képernyős "ablakát" jelenti. Ki kell választanunk az aktivitás típusát, ez lehet üres, vagy akár már különböző célokra előkészített. Ezek után megadjuk az aktivitás nevét, ilyen névvel fog létrejönni a hozzátartozó java osztály is, illetve az elrendezés nevét. Az elrendezés az azonos nevű .xml fájl lesz mely a fentebb taglalt négy fő mappa közül a res/layout mappába fog kerülni. Egy activity kinézetét (elrendezését), szakszóval layout-ját az Android Studio Layout Editorjával tudjuk egyszerűen testreszabni. Segítségével módosítani tudjuk az xml-es fájlban a kinézetet, illetve grafikusan is tudjuk fogd és vidd módszerrel szerkeszteni. Az eszköztárból kiválasztjuk a kívánt elemet, mint például szöveges mező vagy gomb, majd behúzzuk egy virtuális telefon képernyőre.
2.2.2. Android SDK Az Android SDK letölthető külön is a Google szervereiről, viszont az Android Studio telepítésével az SDK is telepítésre kerül, így funkciói könnyedén elérhetőek a fejlesztőkörnyezetből. Első fontos eszköz az SDK Manager, melynek segítségével meg tudjuk nézni a telepített SDK alkotórészeit, azt hogy melyek vannak telepítve, illetve miket tudunk még telepíteni és nem utolsó sorban az elemek frissítését is itt tudjuk elvégezni. Másik fontos eszközünk az AVD (Android Virtual Devices). Itt hozhatunk létre virtuális eszközöket, illetve testreszabhatjuk és megtekinthetjük a már elkészítetteket is. Létrehozhatunk virtuális telefon, tablet, TV, illetve hordható eszközöket is mint az okoskaróra. Beállíthatjuk a rajta futó Android verziószámát, kijelzőjének méretét és felbontását, az elérhető memóriát és még sok minden más dolgot. Ha létrehoztuk a virtuális eszközt ezek után rögtön el is indíthatjuk azt, majd ha elindult az eszköz, a fordítás és futtatás segítségével kiválaszthatjuk a futó virtuális eszközt és ezáltal az alkalmazás lefordításra és telepítésre kerül a virtuális eszközre. Az Android Device Monitor segítségével megfigyelhetjük az eszközünk teljesítményét, miközben az applikációnk fut. Egyaránt alkalmas virtuális és usb-n keresztül csatlakoztatott fizikai eszközök megfigyelésére is. A számunkra legfontosabb megfigyelhető erőforrás az eszközön belül a logcat. Ez egy olyan része az Androidnak mint windowsos környezetben a konzol. Ide írja ki az esetleges hibákat applikációnk, de mi is írhatunk ki szöveget ide. Be tudjuk állítani hogy mely éppen futó alkalmazás logcat beli bejegyzéseit figyelje, illetve ha mi iratunk ki valamilyen szöveget java kódból azt elláthatjuk különböző címkékkel, így a figyelt címkéket is beállíthatjuk. Monitorozhatjuk még a 11
2.3. Androidos fejlesztés fogalmainak ismertetése lefoglalt memóriamennyiséget is, a felhasznált cpu vagy gpu időt is, illetve a hálózati adatforgalmat is. Fontos funkciója még a képernyőfelvétel készítése az eszközről. Tartalmaz még Debugolási eszközöket is melyek segítségével töréspontokat helyezhetünk el a kódban melynek hatására a futás megáll. Figyelhetünk még változókat is, annak értékeit.
2.2.3. Gradle Scriptek A Gradle egy projekt építő eszköz amely kezeli a függőségeket és lehetőségünk van testreszabni a projektépítés menetét. A Gradle a fordítás és futtatás menetekor a háttérben fut, elkülönülten az Android Studiótól, így projektünk módosításakor a parancssorból is újra tudjuk építeni a projektet, akár egy olyan számítógépen ahol nem található Android Studio. Az építési beállításokat build.gradle fájlokban tárolhatjuk. Ezen fájlok külön vannak minden egyes modulhoz. Van egy fő gradle fájl a projektünkhöz is. A modulunk gradle fájljában tudjuk megadni a minimum SDK verziót, az alkalmazásunk azonosítóját (classpath-át), illetve itt tudjuk hozzáadni az esetleges support csomagokat is. Ezen support csomagok feladatai, hogy az alacsonyabb verzióval fejlesztett alkalmazásoknak támogatást nyújtson a magasabb verziójú szolgáltatásokkal szemben. Vagyis egyes funkciók alacsonyabb verziószám esetében csak ezen csomagok segítségével vehető igénybe. A Gradle fő előnye, hogy figyeli a java kódot és ha olyan osztályt, vagy metódust talál amely nem elérhető a minimum SDK-ban megadott verzióban informálja a programozót.
2.3. Androidos fejlesztés fogalmainak ismertetése 2.3.1. Activity Az Activity vagy aktivitás egy alkalmazás komponens, ami képet jelenít meg a kijelzőn, amivel a felhasználó interakcióba tud lépni, mint például tárcsázhat egy telefonszámot, kamerázhat, elküldhet egy emailt vagy böngészhet a térképen. Minden activity-nek van egy saját ablaka amin belül megjelenik a felhasználói felülete. Ez az ablak általában betölti a kijelzőt, de akár kisebb is lehet mint a kijelző vagy rajta lehet (lebeghet) más ablakokon is. Egy alkalmazás általában több ilyen activity-ből állhat, melyek egymással összeköttetésben lehetnek. Alkalmazásunk kezdőképernyője azon aktivitás lesz, mely meg van adva fő aktivitásnak. Minden aktivitás indíthat másik aktivitást is, ha más jellegű dolgot szeretnénk csinálni az alkalmazásunkban. Mikor egy új aktivitás elindul, akkor az előző aktivitás leáll, de a rendszer az erőforrásait fenntartja egy úgynevezett verembe továbbra is. Ez a verem úgy működik mint a programozásban használatos más vermek, vagyis mindíg egymásra rakodunk és kivételkor mindig a legfelsőt vesszük. Ebből látszik, hogy ha a felhasználó végzett egy aktivitással és megnyomja a vissza gombot készülékén akkor a veremből a legfelső elem megsemmisítésre kerül (hiszen ez a befejeződő aktivitás) és az alatta levő elem vagyis aktivitás előrekerül, megjelenik a felhasználói felülete. Ha egy aktivitás futása megállításra kerül mert egy másik aktivitás indult, akkor a régi aktivitás értesítve lesz erről, a különböző callback metódusok segítségével. Sok callback metódusa lehet egy aktivitásnak amely állapotváltozáskor hívódik meg. Ilyen állapotváltozások lehetnek a létrehozás, megállítás, visszatérés vagy megsemmisítés. Ezen állapotváltozások mindegyikéhez tartozik egy callback metódus melyeknek segítségével reagálhatunk az állapotváltozásra mint például ha megállításra 12
2.3. Androidos fejlesztés fogalmainak ismertetése kerül akkor elengedünk bizonyos erőforrásokat, valamint visszatérésnél megigényelünk erőforrásokat. Ezeket az állapotváltozásokat az aktivitás életciklusának is nevezzük. Egy új aktivitás létrehozásához egy Activity osztályból származó osztályt kell létrehoznunk. Ebben az osztályban implementálni kell a fentebb említett callback metódusokat, amelyeket a rendszer hív ha állapotváltozás történik az aktivitás életciklusában. A két legfontosabb callback metódus a következők: • onCreate(): Ezt a metódust mindenképpen implementálni kell. A rendszer az aktivitás létrehozásakor hívja meg. Ebben a metódusban érdemes inicializálni a futáshoz elengedhetetlen változókat és az alkalmazás különböző egységeit. Legfontosabb, hogy ebben a részben kell meghívni a setContentView() metódust is, amely betölti az xml fájlban definiált elrendezését a felhasználói felületnek. • onPause(): Ez akkor kerül meghívásra, ha a felhasználó elkívánja hagyni az aktivitást. A felhasználó miután elhagyta az akitivitást nem garantált, hogy vissza fog térni ide az alkalmazásunk élete alatt, ezáltal itt kell lementeni minden olyan változást amit a felhasználó eszközölt, amelyet szeretnénk hogy alkalmazásunk megsemmisítése után is megmaradjanak. A felhasználói felületet úgynevezett view-ek fájával lehet reprezentálni. Ezen objektumok mind a View osztályból származnak. Minden egyes view vagy nézet egy maghatározott helyű és méretű, kijelzőn látható négyzet alakú részt irányít, vagyis reagál a felhasználó műveleteire. Egy nézet lehet például egy gomb mely egy akciót indít el ha a felhasználó rá kattint. Az Android számos már előre elkészített nézetet tartalmaz, melyet használhatunk a kinézet testreszabására. Ezek az úgynevezett widget-ek melyek megjelenhetnek és viselkedhetnek úgy mint egy gomb, szöveges mező, rádiógomb vagy kép. Az elrendezések vagy layout-ok is nézetek, melyek a ViewGroup osztályból származnak. Feladatuk hogy elrendezést nyújtsanak a bennük elhelyezkedő gyerek nézeteknek. Ilyenek lehetnek a linear layout, grid layout vagy relative layout. A legelterjedtebb megoldás hogy definiáljunk egy kinézetet aktivitásunknak, ha azt egy xml layout fájlban tesszük. Ennek a fájlnak az alkalmazás erőforrás mappájában a helye. Ezen módszer előnye, hogy így jól elkülöníthető az alkalmazásunk felhasználói felülete annak viselkedésétől. A fentebb említett setContetView() metódus segítségével tudjuk hozzácsatolni ezen xml fájlt az osztályunkhoz. Létrehozhatunk View és ViewGroup objektumokat a forráskódból is és megadhatjuk azokat is ilyen módon felhasználói felületnek. Mindenek előtt ahhoz hogy a rendszer számára is hozzáférhető és létrehozható legyen egy aktivitás deklarálni kell azt a manifest fájlban. Ezt úgy tehetjük meg hogy a manifest fájl application részébe egy activity tagot rakunk. Ebben a tagban attribútumai segítségével megadhatjuk a címét az aktivitásunknak, ikonját vagy témáját. Az android:name attribútumot kötelező megadni, ez adja meg az osztályának a nevét. Intent-filter tagokban tudjuk megadni azt hogy más alkalmazások komponensei hogyan aktiválják azt. Ebben a tagban az action-ban megadva a main-t specifikálja a rendszernek hogy ez a fő aktivitás. A category tagban megadhatjuk launcher névvel, hogy a rendszer ezen aktivitás ikonját illetve címét jelenítse meg ahonnan a felhasználó elindítja az applikációnkat. < manifest ... > < application ... > 13
2.3. Androidos fejlesztés fogalmainak ismertetése < activity android : name = " . PéldaActivity " / > ... application ... > ... manifest > Ahol a manifest tag az xml fájl gyökere, az application tag az alkalmazásunkra vonatkozó tagokat foglalja össze, és az activity tag a programunk egyetlen, PéldaActivity nevű aktivitását deklaráló tag.
2.3.2. Fragmentek A fragment feladata egy viselkedés vagy felhasználói felület részének megvalósítása egy aktivitásban. Segítségével több fragmentet tudunk kombinálni egyetlen aktivitásban ezáltal egyszerűsödik a felhasználói felület. Ezeket a fragmenteket aztán másik aktivitásban is újrahasznosíthatjuk. Felfoghatjuk a fragmenteket moduláris részének egy aktivitásnak, aminek külön életciklusa van, saját maga reagálhat eseményekre, illetve az aktivitás futása közben tudjuk megjeleníteni vagy eltüntetni. Egy fragmentnek mindig kell, hogy hozzá legyen adva egy aktivitáshoz, ebből látszik hogy életciklusára közvetlen befolyással van az aktivitás életciklusa. Például, ha az aktivitás le van állítva ekkor a benne levő összes fragment is le lesz állítva, illetve ha törtlésre kerül, a benne levő fragmentek is törlésre kerülnek. Viszont ha az aktivitás fut, ekkor tudjuk manipulálni a fragmentek függetlenségét, vagyis hozzá tudunk adni illetve el tudunk távolítani fragmenteket. Ha egy fragmentet hozzáadunk az aktivitásunk felületéhez, akkor az egy ViewGroup osztályon belül lesz megtalálható, ahol a kinézetet, a hierarhiát már maga a fragment hozza létre. Egy fragmentet a
tag elhelyezésével az aktivitás xml-jében tudunk létrehozni. Java kódból a fentebb említett ViewGroup osztályba való betöltéssel adhatunk hozzá. Nem minden esetben kell rendelkeznie a fragmentnek saját felhasználói felülettel, ha nincs felülete akkor azt egy láthatatlan dolgozó (olyan programrész mely a futáshoz szükséges általában a hosszú számításigényes műveleteket végzi) részegységének tekinthetjük az aktivitásunkon belül. public static class ExampleFragment extends Fragment { @Override public View onCreateView ( LayoutInflater inflater , ViewGroup container , Bundle savedInstanceState ) { return inflater . inflate ( R . layout . example_fragment , container , false ) ; } } A fenti példaosztály a Fragment osztályból származik, mely betölti a felhasználói felületet az example_fragment.xml fájlból. Az onCreateView metódus autómatikusan meghívásra kerül, ha a Fragment egy már éppen futó Activity-ben betöltésre kerül. Megkapja paraméterében az inflater objektumot, melynek feladata a felhasználói felület betöltése a fentebb említett fájlból. A container tartalmazza azt a ViewGroup osztályt melyben betöltés után a fragmentünk látható és elérhető lesz, a savedInstanceState egy Bundle objektum mely tartalmazza a szükséges információkat ha ez a fragment nem 14
2.3. Androidos fejlesztés fogalmainak ismertetése először lenne létrehozva, hanem resume állapotátmenetből lenne hívva.
2.3.3. Intentek és Intent filterek Az Intent egy üzenetküldő és fogadó objektum amit különböző alkalmazás komponensek között hasznosíthatunk. Az intentek arra valók hogy komminukációt létesítsünk különböző komponensek között. Három fő felhasználási területe van: • Új aktivitások indítása: – A startActivity() metódus paraméteréül adva egy Intent objektumot. Ez az intent értesíti az aktivitást, hogy induljon el, illetve tartalmaz minden szükséges adatot. – Ha azt szeretnénk hogy valamilyen visszajelzést adjon az új aktivitás amikor befejeződik, akkor hívhatjuk a startActivityForResult() metódust. A szülő aktivitás kap egy másik Intent objektumot az onActivityResult() callback metódusán keresztül amiben megtalálható a szükséges visszatérési információ. • Új szolgáltatás indítása: – A Service objektum egy komponens ami különféle operációkat végez a háttérben, felhasználói felület nélkül. Elindíthatunk egy egyszeri operációt a startService() metódus paraméteréül adva egy intent objektumot. • Broadcast küldése: – A broadcast egy üzenet amit bármelyik alkalmazás megkaphat. A rendszer is különféle broadcastokat küld más rendszer alkalmazás számára, mint például rendszer bootolásnál, vagy az eszköz töltőre való helyezésekor. A sendBroadcast() segítségével tudjuk ezt megvalósítani, paraméterében egy Intent-el. Két típusú intentről beszélhetünk: explicit és implicit intentekről. Az explicit intent a nevével hivatkozik a komponensre ha indítani kell. Ezt akkor használjuk, ha a saját alkalmazásunkban indítunk komponenseket, hiszen ekkor tudjuk az osztály nevét amelyet indítani szeretnénk. Explicit intent például új aktivitás indítása a felhasználó interakciója révén, vagy egy szerviz indítása. Implicit intentről akkor beszélhetünk ha nem a nevén specifikálunk egy komponenst, hanem egy akciót hozunk létre, amelyre válaszolhat a kért komponens, amely általában egy másik alkalmazásban található meg. Erre példa, ha meg szeretnénk mutatni a felhasználónak egy pozíciót a térképen, implicit intent segítségével kérhetünk egy másik, erre megfelelő alkalmazást hogy megmutassa azt. Ha implicit intentet hozunk létre, az Android rendszer megkeresi a megfelelő komponenst, ezek után pedig összehasonlítja a megtalált komponens alkalmazásának manifest xml fájljában deklarált intent filterek és a létrehozott intent közötti egyezést. Ha a létrehozott intent egyezik a filterrel, a rendszer elindítja a komponenst és átadja neki az Intent objektumot. Ha több komponens filterébe is beleillik az intentünk akkor a rendszer dialógus ablakban listázza a lehetséges komponensek listáját, amelyből a felhasználó döntheti el a neki megfelelőt.
15
2.3. Androidos fejlesztés fogalmainak ismertetése Intent downloadIntent = new Intent ( this , DownloadService . class ) ; downloadIntent . setData ( Uri . parse ( fileUrl ) ) ; startService ( downloadIntent ) ; A fenti kódrészlet példa egy explicit intent-re. Létrehozunk egy új Intent objektumot, első paraméternek megadva az intent kontextusát (ebben az esetben a this azért lehetséges mert az Intentet egy Activity osztályon belül használjuk, így annak kontextusa maga az aktivitás) Második paraméternek a cél-osztály nevét kell megadnunk, példánkban ez egy letöltő szerviz osztály. A setData metódussal beállítjuk az intent adatait, itt a fileUrl egy letöltendő fájl webes címe. Utolsó utasításként pedig elindítjuk a szolgáltatást. Ezek után tekintsünk meg egy implicit esetet: Intent sendIntent = new Intent () ; sendIntent . setAction ( Intent . ACTION_SEND ) ; sendIntent . putExtra ( Intent . EXTRA_TEXT , textMessage ) ; sendIntent . setType ( " text / plain " ) ; if ( sendIntent . resolveActivity ( getPackageManager () ) != null ) { startActivity ( sendIntent ) ; } Első sorban létrejön az Intent, ezek után beállítjuk a szükséges paramétereket: az akciót, mely a példában az ACTION_SEND mely egy egyszerű akció melynek segítségével adatokat oszthatunk meg más alkalmazásokkal. A putExtra segítségével beállítjuk az elküldött String-et setType-al pedig a típust adjuk meg. Ezek után az if rész nélkül való intent indítása esetén, ha a felhasználó eszközén nem található olyan alkalmazás mely a kérést fogadni és feldolgozni tudná alkalmazásunk működése leáll. Ezért ellenőrizzük a resolveActivity segítségével hogy található-e a rendszerben megfelelő alkalmazás.
2.3.4. Erőforrások Alkalmazásunk erőforrásait mint például a képeket szövegeket mindig érdemes elkülöníteni a kódtól, így azt függetlenül tudjuk módosítani minden mástól. Ezen szemléletnek másik legnagyobb előnye, hogy más erőforrásokat tudunk biztosítani más és más beállítású eszközökhöz, mint például változhat a nyelvi beállítás, illetve a kijelző mérete is ehhez mérten pedig a megjelenített képek felbontása is. Ennek a kivitelezéséhez a projekt res/ mappáján belül az erőforrás típusától függően különféle almappákban kell tárolnunk azokat. Alapértelmezett erőforrásokról beszélünk, ha nem változik eszköz beállításától függően, vagyis minden eszközhöz ugyan az az erőforrás tartozik. Alternatív erőforrás az, ha azt egy kifejezett beállítású eszközre tervezzük. Az egy eszközre szánt erőforrások mappájának neveit ugyanazon azonosító ellátásával hozhatjuk létre. Például ha egy felhasználói felület layoutja a res/layout mappában van specifikálhatunk egy res/layout-land mappát is, ekkor ha elfordítjuk az eszközünket a rendszer a land azonosítóval ellátott layoutot tölti be automatikusan. Erőforrásokhoz való hozzáférés a R nevű osztályon keresztül lehetséges. Minden erőforrás ID-ja megtalálható ebben az osztályban, az aapt eszköz automatikusan generálja 16
2.3. Androidos fejlesztés fogalmainak ismertetése le. Ha az alkalmazásunk lefordításra kerül az aapt legenerálja az R osztályt, melybe minden ID-val ellátott /res mappában található erőforrást elhelyez. Az R osztályban minden egyes erőforrásfajtához tartozik egy alosztály, mint például az R.drawable melybe minden olyan erőforrás van ami kirajzolható a képernyőre. Ezen alosztályon belül minden egyes erőforrásra van egy statikus integer (például R.drawable.icon). Ez az integer az ID-ja az erőforrásnak mely segítségével java kódban lekérhetjük azt. Tehát az R fájlban van minden egyes erőforrás azonosítója, de közvetlenül soha sem érdemes belenézni egy azonosítót keresve. Ennek oka, hogy minden azonosító megismerhető névvel keről létrehozásra: • Erőforrás típus: Minden erőforrás kategorizálva van típusonként, mint például string, drawable vagy layout. • Erőforrás név, ami lehet fájlnév vagy egy xml-ben megadott android:name attribútum ha az erőforrás egy egyszerű típus mint string. Kétféle képpen tudunk hozzáférni egy erőforráshoz: • Kódból: A statikus integer-t használva az R osztály egyik alosztályából, mint például R.sting.hello, ahol a string a típus és hello az erőforrásnév. • XML-ből való hozzáférés: Speciális szintaktikát használva az xml-ben amely az R osztályban lévő erőforrásra hivatkozik, például @string/hello. Kódból való hozzáférés esetén a metódus paraméterében megadva az ID-t. Például egy ImageView kép megjelenítsre használt nézet osztálynak tudjuk beállítani a megjelenített képet a következőképpen: ImageView imageView =( ImageView ) findViewById ( R . id . myimageview ) ; imageView . setImageResource ( R . drawable . myimage ) ; findViewById() metódus segítségével a paraméterében megadva az id nevét lekérdezzük a view-t és a setImageResource() metódussal beállítjuk a myimage nevű drawable típusú erőforrást. Más erőforrásokat is lekérdezhetünk a Resources osztály getResources() metódusával. A hivatkozás szintakszisa a következő: [<package_név>.]R.<erőforrás_típus>.<erőforrás_név> • <package_név> a csomag neve amely elhagyható ha ugyan azon csomagból hivatkozunk rá • <erőforrás_típus> az R-ben található alosztály neve • <erőforrás_név> lehet az erőforrás fájlneve kiterjesztést elhagyva vagy az android:name attribútumban megadott érték az xml fájlban.
17
2.3. Androidos fejlesztés fogalmainak ismertetése Másik módja a hozzáférésnek az XML-ből való hozzáférés. Ezt akkor használjuk gyakran, ha a felhasználói felületet xml fájlból állítjuk elő. Például ha egy gombot adunk hozzá az elrendezéshez, mindig érdemes a gomb feliratát string erőforrásban megadni. < Button android : layout_width = " fill_parent " android : layout_height = " wrap_content " android : text = " @string / submit " / >
2.3.5. A manifest fájl Minden alkalmazásnak lennie kell egy AndroidManifest.xml nevű fájlnak, a gyökérkönyvtárban. A manifest fontos információkat tartalmaz az alkalmazásról az Android rendszer számára, olyanokat, melyeknek mindenképpen jelen kell lenniük mielőtt az applikáció elkezdhetné futását. A manifest fájlnak sok fontos dolga van: • Hozzárendeli a Java csomag nevét az applikációnkhoz. A csomag neve azonosítja alkalmazásunkat a rendszeren belül, ezáltal ennek egyedinek kell lenni. • Leírja az applikáció komponenseit - az aktivitásokat, szolgáltatásokat, broadcast vevőket. Felsorolja az osztályok neveit melyek a különböző komponensek működését megvalósítják, illetve felsorolja az osztály képességeit (például hogy mely Intentek lekezelésére képes). Ezen deklarációk segítségével tudja az Android rendszer hogy mely komponensek, milyen feltételek létrejöttekor indíthatóak el. • Leírja, hogy mely folyamatokon mely komponensek fognak futni. • Deklarálja, hogy milyen hozzáféréssel rendelkezik az alkalmazásunk, ezáltal döntheti el a rendszer hogy mely védett API részekhez férhetünk hozzá. • Ezenfelül azokat a hozzáférési jogokat is kiköti, amelyek kellenek más alkalmazásoknak ha a mi applikációnk valamely komponensét kívánják használni. • Deklarálja a minimum szintjét az Android API-nak amely szükséges a futáshoz. • Listázza azon könyvtárakat, amelyek kellenek az alkalmazásnak, ezáltal betöltésre kerülnek azok is.
2.3.6. Felhasználói felület Minden egyes elem a felületen a View és ViewGroup osztályok segítségével vannak felépítve. A View egy objektum amely valamit a képernyőre rajzol, így a felhasználó interakcióba tud lépni vele. A ViewGroup olyan objektum mely View osztályokat tartalmaz, vagy további ViewGroup osztályokat tartalmaz és így defíniálják a megjelenést. Az Android számos View és ViewGroup osztályból származó osztályokat tartalmaz, melyek különféle beviteli módokat és elrendezési formákat valósítanak meg. Alkalmazásunk felhasználói felülete View és ViewGroup objektumok hierarchiájának megadásával képzelhető el, ahogyan a lenti ábra mutatja. Minden egyes view csoport egy láthatatlan tároló, mely megadja a gyerekeinek az elrendezését, míg a gyerekei 18
2.3. Androidos fejlesztés fogalmainak ismertetése lehetnek egyszerű beviteli eszközök, vagy további csoportok melyek a felület további részeit definiálják. Ez a fa hierarchia lehet annyira egyszerű, vagy összetett amennyire annak szükségességét érezzük.
Egy elrendezés deklarálásához, létrehozhatunk View objektumokat a kódunkban melyekkel elkezdhetjük építeni a fánkat, de a legegyszerűbb és legcélravezetőbb módja ennek, ha azt az elrendezés xml fájlában tesszük. Az xml sokkal ember-barátabb formában mutatja az elrendezését a felületünknek. Az egyes xml tagok nevei megegyeznek a java kódban elérhető osztályok neveivel, vagyis a tag java kódban elérhető osztály neve a TextView. Egy egyszerű vertikális elrendezés xml-fájlja, mely tartalmaz egy szöveges beviteli mezőt és gombot a következőképpen nézhet ki: xml version = " 1.0 " encoding = " utf -8 " ? > < LinearLayout xmlns : android = " http :// schemas . android . com / apk / res / android " android : layout_width = " fill_parent " android : layout_height = " fill_parent " android : orientation = " vertical " > < TextView android : id = " @ + id / text " android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : text = " I am a TextView " / > < Button android : id = " @ + id / button " android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : text = " I am a Button " / > LinearLayout > Mikor a felület betöltésre kerül az alkalmazás kódjában (megadva erőforrásként az xml fájlt) a rendszer minden egyes csomópontját futásidejű objektummá alakítja, amely tartalmazza az xml-ben megadott tulajdonságokat.
2.3.7. Elrendezések A layout vagy elrendezés a struktúráját írja le a felhasználói felületünknek, mint például egy aktivitásnak vagy alkalmazás widget-nek. Elrendezést kétféle képpen tudunk létrehozni: • A felhasználó elemeket az xml-ben hozzuk létre: Az Android egyszerűen társítja hozzá az xml elemeket a View osztályhoz és alosztályaihoz. 19
2.3. Androidos fejlesztés fogalmainak ismertetése • Futásidőben hozzuk létre az elemeket: Alkalmazásunk View és ViewGroup osztályokat hozhat létre futásidőben. Az Android keretrendszer lehetővé teszi hogy egyszerre is használhassuk ezen két módszert. Például deklarálhatjuk az alapértelmezett elrendezést alkalmazásunknak az xml fájlban, megadva az elemeket és tulajdonságait amelyek megjelennek a kijelzőn. Ezek után java kódok hozzáadásával az alkalmazásunkban módosítani tudjuk az elemek megjelenését futás-időben. A fő előnye az xml-ben való deklarálásnak, hogy segítségével jobban el tudjuk különíteni alkalmazásunkban a felhasználói felületet megvalósító részt, a mögötte levő viselkedések megvalósításától. A felhasználói felület független az alkalmazás kódjától, ami azt eredményezi, hogy módosítani tudjuk kinézetét, a kód módosítása nélkül. Például létre tudunk hozni különálló elrendezéseket orientációtól, kijelző mérettől és nyelvtől függően. Nem utolsó sorban xml-ben egyszerűbb vizualizálni a kinézetet, ezáltal a hibajavítás is egyszerűbbé tehető. Xml írása közben egyszerűen tudjuk felépíteni a megjelenítést, hasonló módon mint html formátumban weblapok létrehozásakor. Minden egyes layout fájl egyetlen gyökér elemet tartalmazhat, amelynek egy View vagy ViewGroup-nak kell lennie. Ha a gyökér elem meghatározásra került, további gyerek elemeket adhatunk hozzá, melyek tovább finomítják a megjelenést. Ha elkészültünk az xml fájlal, a projekt mappánk res/layout mappájába kell mentenünk .xml kiterjesztéssel. Mikor az applikáció fordításra kerül, minden egyes xml elrendezés fájl egy View erőforrásra alakul. Az elrendezés erőforrást ezután az alkalmazás Activity.onCreate() callback metódusában kell betölteni. Ezen metóduson belül meghívva a setContentView() paraméterül adva a layout erőforrás azonosítóját betöltődik a felület. Például ha a xml layout main_layout.xml fájlnéven lett elmentve a következő képpen tudjuk betölteni: public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . main_layout ) ; } Minden egyes View és ViewGroup támogatja a saját xml attribútumait. Egyes tulajdonságok speciálisan egy meghatározott View objektumhoz kapcsolódik (például a TextView támogatja a textSize attribútumot), de ezen tulajdonságok öröklődnek a gyerek View osztályokba is. Vannak közös tulajdonságok melyek minden View objektumban megtalálhatóak, mivel ezen tulajdonságok a View ősosztályában vannak definiálva. Ilyen attribútum az id például. Más attribútumok is vannak mint például a layout paraméterek, amelyek olyan tulajdonságok amelyek a szülőben megtalálható View objektum elrendezéséhez kötődnek. Minden egyes objektum rendelkezik egy hozzá kapcsolt egész id-val mely egyedileg azonosítja a hozzátartozó View objektumot a fán belül. Mikor az alkalmazás fordításra kerül, egy egész számmal lesz reprezentálva, viszont az id tipikusan egy string-el lesz párosítva az xml elrendezésen belül az id attribútumon keresztül. Ez a tulajdonság közös minden View objektumnak és nagyon gyakori a használata. A szintaxisa egy id-nak egy xml tagon belül: android : id = " @ + id / my_button " 20
2.3. Androidos fejlesztés fogalmainak ismertetése A kukac-jel a string elején jelzi az xml feldogozónak hogy dolgozza fel és párosítsa össze a stringet az egyedi azonosítóval. A plusz szimbólum jelzi hogy ez egy új erőforrás, így létre kell hozni és hozzá kell adni az R.java fájlban levő erőforrások közé. Mikor egy Androidos ID erőforrásra hivatkozunk nem kell a plusz szimbólum, viszont hozzá kell adni az android csomag névterét a következő képpen: android : id = " @android : id / empty " Az android névtérrel a helyén, így már az android.R osztályban levő erőforrásra hivatkozunk nem pedig egy másik lokális erőforrásra. Új view létrehozásához és az alkalmazáson belüli hozzáféréséhez a következő módszer a célravezető: 1. Létrehozzuk a view-t egy egyedi ID-val: < Button android : id = " @ + id / my_button " android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : text = " @string / my_button_text " / > 2. Létrehozzuk az objektumot és begyűjtjük az elrendezésből azt: Button myButton = ( Button ) findViewById ( R . id . my_button ) ; ID meghatározására mindenképpen szükség van RelativeLayout létrehozásakor. Egy relatív elrendezésben a testvér view-ok elrendezését egymáshoz viszonyítva adhatjuk meg, mellyel ID-val hivatkozhatunk. Az ID-nak nem kell mindenképpen egyedinek lenni az egész fában, viszont abban a fa-részben ahol keresni szeretnénk abban muszáj annak lennie. Az XML elrendezés attribútumok layout_valami névvel vannak ellátva és a View elrendezés paraméterét adják meg attól függően hogy ez a View milyen elrendezésű ViewGroup-ban található meg. Minden egyes ViewGroup osztály implementál egy beágyazott osztályt amely a ViewGroup.LayoutParams osztályból származik. Ezen alosztályban megtalálhatóak azon tulajdonságok melyek leírják a méretét és elrendezését minden egyes gyerek view csoportnak. A lentebb látható ábrán látszik, hogy a szülő LinearLayout paraméterekkel látja el gyerekeit.
21
2.3. Androidos fejlesztés fogalmainak ismertetése Minden view group -nak kell lennie magasság és szélesség attribútumoknak (layout_width és layout_height). Ezen attribútumokat pontos értékekkel is elláthatjuk, viszont nem ez a leggyakoribb módszer. Helyette a következő két értéket állíthatjuk be: • wrap_content segítségével a view mindig akkora méretű lesz amekkorára a benne lévő gyerek view-eknek szükségük van. • match_parent használatakor a view akkora méretű lesz amekkorát megenged neki a szülő view. Ha pontos értékekkel állítjuk be ezen értékeket, mint például pixel számmal az nem ajánlott. Helyette használhatunk pixelsűrűség-független pixel egységeket röviden dpt. Ezen mértékegység biztosítja, hogy alkalmazásunk minden eszközön - függetlenül a kijelző méretétől - ugyan úgy fog megjelenni. Említettük hogy van lineáris elrendezés és relatív elrendezés. Ezeken felül fontos nézet még a lista nézet. A ListView egy olyan nézet csoport, amely elemek egy görgethető listáját jeleníti meg. A lista elemei autómatikusan kerülnek beszúrásra az Adapter osztály segítségével, mely a tartalmat egy tömbből, vagy adatbázisból nyeri és a kinyert adatokat átalakítva a listában megjeleníthető view-ba konvertálja.
22
3. fejezet Az alkalmazás fejlesztésének lépései 3.1. A tervezés fontos lépései Első fontos kérdés az alkalmazás platformja. Lehetséges platformként felmerült a Windows Phone, iOS és Android. Windows Phonera való fejlesztés alapvető programozási nyelve a C++ és C#, így ezen platform számomra nem volt megfelelő. iOS-re való fejlesztés alapvető eszköze egy Mac gép mely nem elérhető számomra, illetve a programozási nyelve is az Objective C. Így a számomra legmegfelelőbb platform az Android. Ez a platform fut a legtöbb eladott okostelefonokon karöltve az iOS-el, így alkalmazásom széles körben elérhetővé válhat.
3.2. Fogalmak Az elkövetkező fejezetben néhány fontos, az alkalmazás fejlesztéséhez szükséges alkalmazás-specifikus fogalmak kerülnek bevezetésre: • Alapanyag: Olyan táplálék, mely táplálék és egyben alapanyag is. • Táplálék: Két fajtája van: az olyan táplálék mely már alapból megtalálható az alkalmazás adatbázisában - ezen táplálékok között találhatóak meg az alapanyagok is - illetve a felhasználói Táplálékok, melyet a felhasználó hozott létre alapanyagok kombinálásával és ezek már alapanyagok nem lehetnek. Mindkét típus kalória, energia, szénhidrát, zsír, fehérje és rostból áll. • Napló: Ebben találhatóak meg a táplálékok, napokra felbontva. • Kereső: Olyan ablaka az alkalmazásnak melyben a felhasználó keresni tud táplálékokat. Ezen kereső lehet akár leszűkítve alapanyagokra is.
3.3. Tervezett funkciók bemutatása Alkalmazásom fő feladata az étkezési adatok számontartása a felhasználónak. A naplóba - mely a program főoldalának felel meg - lehetősége van a felhasználónak hozzáadnia táplálékokat a naplóba. Keresés segítségével tud a felhasználó az előre beépített, közel 300 táplálék közül választani. A választás után a táplálék fajtájától függően megadhatja a mennyiségét a tápláléknak, gramm, mililiter, vagy adag formájában. Kiválasztás 23
3.3. Tervezett funkciók bemutatása után a felhasználónak lehetősége van megtekinteni a mennyiségtől függően változó, táplálékban levő anyagok összetételét, mint például kalória, energia, szénhidrát vagy rost. Hozzáadás után a felhasználó szabadon változtathat a mennyiségen is. A főképernyőn lekérdezhető a napi összesítés is, illetve eltávolíthatóak is a nem kívánatos táplálékok. Második fő funkciója a már meglévő táplálékokhoz való újabb ételek hozzáadása. A felhasználó egyszerűen tud létrehozni új ételeket: hozzáadhat alapanyagokat a kívánt ételéhez, olyan módon mintha egy receptes könyv hozzávalóit böngészné. Ezen alapanyagok az előre beépített táplálékok egy olyan szűkebb csoportja melyek alapanyagok is lehetnek. Ezek után a program a hozzáadott alapanyagok összetételét összeadja, így létrejön egy új táplálék. A felhasználó archiválni is tudja a heteket, ezáltal könnyen megtekinthető a már elmúlt hetekben fogyasztott táplálékok összetételei is. A következő táblázat a funkciók listáját és rövid ismertetésüket tartalmazza:
Napló kezelése
Az alkalmazás főképernyője a napló, melyben a felhasználó megtekintheti heti bevitt tápértékeit, illetve innen érhet el minden más funkciót is. A napló a hét napjaiból áll és táplálékokat minden egyes napra szúrhat be. Meg kell valósítania a naplóba való táplálékok hozzáadását, törlését és napi tápértékek megjelenítését. Hozzáadáskor keresés segítségével tud a felhasználó a táplálékok közül választani. A választás után a táplálék fajtájától függően megadhatja a mennyiségét a tápláléknak, gramm, mililiter, vagy adag formájában.
Napi információ megjelenítése
Megvalósítja a naplóban levő kért napi információk megjelenítését, vagyis a napban található táplálékok összetevőnkénti összesítését.
Hét archiváló
Ezzel a funkcióval a felhasználó elmentheti a héthez hozzáadott táplálékokat. Megvalósítja az archívum fájlba mentését, illetve a fájlból való listába töltést, vagyis a megjelenítést.
Táplálék keresése
Ezen funkció alapvető, mivel segítségével a felhasználó kereshet a már meglévő táplálékok között. A táplálék összetevői a kalória, energia, szénhidrát, zsír,fehérje és rost mennyiségek. Megvalósítja a név alapján való keresést, illetve az egyes táplálékok információinak megjelenítését is.
Táplálék létrehozása
Segítségével a felhasználó táplálékokat adhat hozzá a már meglévőekhez. Ezt a már meglévő táplálékok kombinálásával érheti el. A program a hozzáadott alapanyagok összetételét összeadja.
24
3.4. Osztályok bemutatása
Adatok menedzselése
Megvalósítja a napló tartalmának fájlba való lementését, ezáltal ha a felhasználó bezárja az alkalmazást nem veszti el "előrehaladását". Az alkalmazás indításakor visszatölti a már lementett naplóban levő táplálékokat. Betölti az úgynevezett permanens adatokat, vagyis azon adatokat mely az alkalmazás részét képezik, vagyis a táplálékok listáját.
3.3.1. Adatok tárolása Első lehetséges tárolási módszer kulcs érték párokban való tárolás. Ezen módszer egyik hátránya hogy primitív típusok tárolására a legalkalmasabb. Egy másik lehetséges adattárolási módszer a fájlok mentése az eszköz belső memóriájába. Ezen módszer tulajdonsága, hogy a fájlok csak a saját alkalmazásunk által elérhető és ha a felhasználó eltávolítja alkalmazásunkat, akkor a lementett fájlok is törlődnek. Menthetünk fájlokat az eszköz külső memóriájára is. Ezen memória lehet eltávolítható memóriakártya, vagy az eszköz belsejében levő, Androidtól elkülönített tárterület. Ennek előnye, hogy a lementett fájlokat egyszerűbben oszthatjuk meg külső alkalmazások számára. Fő hátránya, hogy ezen médiaformátum eltávolítható, így nem mindig érhető el a rendszer számára. Az Android lehetőséget nyújt az adatok tárolására SQLite adatbázisban is. Utolsó lehetséges tárolási módszer hálózaton való adattárolás, viszont ennek nagy hátránya, hogy saját szerver kell ezen adattárolás megvalósítására. A tervezés során a külső memórián való tárolást találtam az alkalmazásomnak legmegfelelőbbnek. Az adatokat json fájlokban fogja tárolni az alkalmazás. A fájlban való tárolás egyszerűbb, az Android api-ja tartalmazza azon osztályokat, melyek segítségével egyszerű a .json fájlok létrehozása és feldolgozása. Az alkalmazás fájljai a rendszer belső memóriáján helyezkednek el, míg a felhasználók adatai a külső tárterületen. Mint az első bekezdésben említem mindkét típusú adat tárolása .json fájlformátumban történik. Ezen formátum hasonlatos az xml adatleíró nyelvhez. Legnagyobb előnye, hogy önleíró nyelv, egyszerű a megértése és programozási nyelv független, vagyis minden elterjedt programozási nyelv tartalmaz segédosztályokat mely segít feldolgozni és építeni .json fájlokat. Az adatok tárolása kulcs és érték párok segítségével történik.
3.4. Osztályok bemutatása A feladat megvalósításához UML-osztály diagramot készítettem, mely a tervezés alapvető eszköze. Segítségével modellezni tudjuk az alkalmazás megvalósításához szükséges osztályokat és a köztük levő kapcsolatokat. A feladat tervezése során felmerült két teljesen elkülönült működési funkció: a felhasználói felület, illetve a mögötte levő adattároló és egyéb funkciókat megvalósító osztályok. Az osztálydiagramot ennek fényében két részletben mutatom be. Első részben a felhasználói felület, második részben az adattárolást és egyéb funkciókat megvalósító osztályok diagramját, illetve azokhoz tartozó leírásokat és programkód-részleteket tekinthetjük meg.
25
3.4. Osztályok bemutatása
3.4.1. Felhasználói felületet megvalósító osztályok
A MainActivity osztály tekinthető az alkalmazás központi osztályának. Fő feladata a napló képernyő megjelenítése, illetve a különböző funkciók és a hozzájuk tartozó felhasználói felületek osztályainak meghívása. Az onCreate() metódusában találhatóak az adat osztályoknak a példányosításai, illetve a hozzájuk tartozó hívások melyek segítségével betöltődnek a felhasználó és alkalmazás adatai. Lentebb láthatóak az onCreate() metódus, illetve a naplóhoz tartozó xml fájl egy részlete mely a felhasználói felületet jeleníti meg: protected void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_main ) ; Toolbar toolbar = ( Toolbar ) findViewById ( R . id . toolbar ) ; setSupportActionBar ( toolbar ) ; NaploModifier . setUp ( this ) ; JsonOperator . setUp ( this ) ; NaploModifier . settingToday () ; 26
3.4. Osztályok bemutatása JsonOperator . loadData () ; try { Adatok . setTaplaleklista ( AdatLoader . loadTaplalekok ( JsonOperator . getData () ) ) ; Adatok . setUserTaplaleklista ( AdatLoader . loadTaplalekok ( JsonOperator . getUserTaplalekData () ) ) ; AdatLoader . loadUserData () ; } catch ( JSONException e ) { e . printStackTrace () ; } NaploModifier . refreshNaploAdatok ( Napok . EGESZ ) ; getSupportActionBar () . setDisplayHomeAsUpEnabled ( true ) ; drawerFragment = ( NavigationDrawerFragment ) getSupportFragmentManager () . findFragmentById ( R . id . fragment_navigation_drawer ) ; drawerFragment . setUp ( R . id . fragment_navigation_drawer , ( DrawerLayout ) findViewById ( R . id . drawer_layout ) , toolbar ) ; } ... < ScrollView android : id = " @ + id / mainScroll " android : layout_width = " match_parent " android : layout_height = " wrap_content " app : layout_behavior = " @string / ap pb ar _ sc ro ll ing_view_behavior " tools : showIn = " @layout / activity_main " tools : context = " . MainActivity " > < LinearLayout android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : background = " @color / colorActivityBackground " android : orientation = " vertical " android : id = " @ + id / mainLayout " > < RelativeLayout android : layout_width = " match_parent " android : layout_height = " 175 dp " android : background = " @drawable / shape_curved " android : layout_margin = " 3 dp " android : id = " @ + id / mainHetfo " > < FrameLayout android : layout_width = " fill_parent " android : layout_height = " 25 dp " android : id = " @ + id / frameLayout " > < TextView android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : textAppearance = 27
3.4. Osztályok bemutatása " ? android : attr / textAppearanceMedium " android : text = " @string / hetfo " android : layout_below = " @ + id / scrollHetfo " android : layout_alignParentLeft = " true " android : layout_alignParentStart = " true " android : layout_gravity = " center " / > FrameLayout > < HorizontalScrollView android : layout_width = " fill_parent " android : layout_height = " 150 dp " android : id = " @ + id / scrollHetfo " android : layout_below = " @ + id / frameLayout " android : layout_toRightOf = " @ + id / frameLayout2 " android : layout_toEndOf = " @ + id / frameLayout2 " > < LinearLayout android : orientation = " horizontal " android : layout_width = " match_parent " android : layout_height = " match_parent " android : id = " @ + id / layoutHetfo " android : padding = " 3 dp " / > HorizontalScrollView > < FrameLayout android : layout_width = " 50 dp " android : layout_height = " 150 dp " android : layout_alignTop = " @ + id / scrollHetfo " android : layout_alignParentLeft = " true " android : layout_alignParentStart = " true " android : id = " @ + id / frameLayout2 " > < ImageButton android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / addHetfo " android : layout_gravity = " top | left | bottom | center_horizontal | right " android : background = " @drawable / ic_add _ ci rcle_outline_black_48dp " android : onClick = " addTaplalek " / > < ImageButton android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / infoHetfo " android : layout_gravity = " center " android : background = " @drawable / ic_info_outline_black_48dp " android : onClick = " napiInfo " / > FrameLayout > RelativeLayout > 28
3.4. Osztályok bemutatása ... LinearLayout > ScrollView > A fentebb látható xml elrendezésben a napló hétfői elrendezése látható. A mainHetfo azonosítójú elrendezésbe töltődik be a felhasználó által kiválasztott NaploModifier osztály által összerakott táplálék view-je. Az onActivityResult() metódus feladata SearchArctivityből való visszatérés esetén a felhasználó által kiválasztott táplálék hozzáadása a naplóba. Ennek a metódusnak egy részlete: ... Bundle bundle = data . getBundleExtra ( " bundle " ) ; int id = bundle . getInt ( " id " ) ; int pos = Adatok . getTaplaleklista () . indexOf ( new Taplalek ( id , null , null , false , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , null ) ) ; Taplalek taplalek = Adatok . getTaplaleklista () . get ( pos ) ; ... TaplalekSzamolt szamoltTaplalek = new TaplalekSzamolt ( taplalek . getId () , taplalek . getNev () , taplalek . getEgyseg () , taplalek . isAlapanyag () , taplalek . getKcal () , taplalek . getEnergia () , taplalek . getSzenhidrat () , taplalek . getZsir () , taplalek . getFeherje () , taplalek . getRost () , mennyiseg , taplalek . getKategoria () ) ; ... Adatok . addTaplalek ( nap , szamoltTaplalek ) ; NaploModifier . refreshNaploAdatok ( nap ) ; A NavigationDrawerFragment osztály valósítja meg az oldalról kihúzható navigáló menüt. A setUp() metódusában beállítása kerül az osztály, egy külön Runnable szálon futó osztály megadásán belül a syncState() metódus hívásával folyamatos szinkronizációban lesz a MainActivity osztályal, így a főképernyőn látható ikon és a kihúzott menü szinkronban lesz. Ennek megvalósítása lentebb látható. A close() metódus segítségével bezárhatjuk a menüt. public void setUp ( int fragmentId , DrawerLayout layout , Toolbar toolbar ) { containerView = getActivity () . findViewById ( fragmentId ) ; mDrawerLayout = layout ; mDrawerToggle = new ActionBarDrawerToggle ( getActivity () , mDrawerLayout , toolbar , R . string . drawer_open , R . string . drawer_close ) { @Override public void onDrawerOpened ( View drawerView ) { super . onDrawerOpened ( drawerView ) ; getActivity () . invalidateOptionsMenu () ; } 29
3.4. Osztályok bemutatása @Override public void onDrawerClosed ( View drawerView ) { super . onDrawerClosed ( drawerView ) ; getActivity () . invalidateOptionsMenu () ; } }; mDrawerLayout . setDrawerListener ( mDrawerToggle ) ; mDrawerLayout . post ( new Runnable () { @Override public void run () { mDrawerToggle . syncState () ; } }) ; } A SearchActivity osztály valósítja meg a táplálékokban való keresés felhasználói felületét. Ezen osztály szoros kapcsolatban áll a SearchAdapter osztállyal. Ennek a feladata a felhasználói felület feltöltése a már előzőleg betöltött táplálékokkal. Az alább látható metódus feladata, hogy az egyes rekordoknak vagyis táplálékoknak Viewt hozzon létre, vagyis egy olyan osztályt mely betölthető a felhasználói felületre. public View getView ( int position , View convertView , ViewGroup parent ) { View vi = convertView ; ViewHolder holder ; if ( convertView == null ) { vi = inflater . inflate ( R . layout . search_list , null ) ; holder = new ViewHolder () ; holder . nev = ( TextView ) vi . findViewById ( R . id . text ) ; holder . info = ( ImageView ) vi . findViewById ( R . id . imageButton ) ; vi . setTag ( holder ) ; } else holder = ( ViewHolder ) vi . getTag () ; if ( data . size () <=0) { holder . nev . setText ( " " ) ; holder . info . setVisibility ( View . INVISIBLE ) ; } else { tempValues = null ; tempValues = ( Taplalek ) data . get ( position ) ; holder . nev . setText ( tempValues . getNev () ) ; holder . info . setVisibility ( View . INVISIBLE ) ; holder . info . setOnClickListener ( new OnItemClickListener ( position ) ) ; vi . setOnClickListener ( new OnItemClickListener ( position ) ) ; } return vi ; }
30
3.4. Osztályok bemutatása A FilterSajat beágyazott osztály segítségével az adapter keresni tud az adatok között, az osztály úgynevezett filtert helyez az adapter listában tárolt adattagjára, ezáltal csak azon táplálékok töltődnek be melyeket a filter megenged. A Communicator interfész feladata a kommunikáció megvalósítása a SearchActivity osztály és a HozzaadoDialog osztályok között. A táplálék hozzáadása időrendi sorrendben a következő képpen történik: 1. A MainActivityben a felhasználó rákattint valamely naphoz tartozó hozzáadó gombra, mely a MainActivity-n belül meghívja az addTaplalek() metódust mely egy intent segítségével elindítja a SearchActivity-t. < ImageButton android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / addHetfo " android : background = " @drawable / ic _add_ c ircle_outline_black_48dp " android : onClick = " addTaplalek " / > public void addTaplalek ( View view ) { Intent intent = new Intent ( this , SearchActivity . class ) ; String napFull = view . getResources () . getResourceName ( view . getId () ) ; napId = napFull . substring ( napFull . indexOf ( " / " ) +1) ; startActivityForResult ( intent , 0) ; } 2. A SearchActivityben létrejön a SearchAdapter, ha a felhasználó keres a listában meghívódik az adapter filter() metódusa. A felhasználó rákattint valamely táplálékra a listában, ekkor a SearchAdapter osztályban OnItemClick esemény váltódik ki, ekkor létrejön egy Bundle melybe elhelyezésre kerül a táplálék azonosítója és átadásra kerül a HozzaadoDialognak. Ezek után megjelenik a dialógusablak. bundle . putString ( " tipus " , tipus ) ; bundle . putInt ( " id " , id ) ; HozzaadoDialog hozzaadoDialog = new HozzaadoDialog () ; hozzaadoDialog . setArguments ( bundle ) ; hozzaadoDialog . show ( searchActivity . getFragmentManager () , " Hozzáadó " ) ; 3. A mennyiség megadása után a Hozzáad gomb lenyomásakor létrejön egy Bundle melybe belekerül a táplálék azonosítója, illetve mennyisége. A SearchActivityben levő Communicator által implementált respond() metódus meghívásával a program futása átkerül a SearchActivitybe, a metódusban átadásra került az elkészült Bundle is. responseBundle . putInt ( " mennyiseg " , 31
3.4. Osztályok bemutatása Integer . parseInt ( mennyisegText . getText () . toString () ) ) ; comm . respond ( responseBundle ) ; 4. A SearchActivity létrehoz egy Intent-et melybe betölti a kapott Bundle-t. Beállítja a saját futásának a végeredményét a setResult() metódus segítségével paraméterében megadva az Intent-et, majd befejezi futását. public void respond ( Bundle data ) { Intent resultIntent = new Intent () ; resultIntent . putExtra ( " bundle " , data ) ; setResult ( Activity . RESULT_OK , resultIntent ) ; finish () ; } 5. Az előbb említett activity futásának befejeződésekor meghívásra kerül a szülő activityben levő onActivityResult() metódus mely esetünkben a MainActivity. Itt a Bundle-ből kiválogatódik a kért táplálék azonosítója mely segítségével kikeresésre kerül a listából a táplálék. A megtalált táplálék TáplálékSzámolt-á alakul a mennyiség segítségével és hozzáadásra kerül a naplóba. A példa rá fentebb megtalálható. A NaploModifier fő feladata a View létrehozása az Adatok osztályba betöltött Táplálék osztályok listája alapján. Miután létrejött a View hozzáadja a MainActivity layout xml-ében levő megfelelő helyre. Az InfoMennyisegDialog egy dialógusablak megjelenítő osztály, mely két dialógusablak megjelenítését tudja végezni: a napi információét, illetve a naplóhoz hozzáadott táplálékok információit megjelenítő dialógust. Utóbbi dialógus második szerepe az esetleges utólagos mennyiségmódosítás is. Az alábbi kódrészlet a hétfői táplálékbevitel információt mutatja: ... viewGlobal = inflater . inflate ( R . layout . infodialog_napi , null ) ; TextView textNev = ( TextView ) viewGlobal . findViewById ( R . id . nev ) ; Double [] lista = new Double [6]; for ( int i =0; i <6; i ++) lista [ i ] = 0.0; Napok nap = null ; if ( bundle . getInt ( " nap " ) == 1) { nap = Napok . HETFO ; textNev . setText ( R . string . hetfoSum ) ; } ... for ( int i = 0; i < Adatok . getSizeNap ( nap ) ; i ++) { lista [0] += Adatok . getIndexOfNap ( nap , i ) . getKcal () ; lista [1] += Adatok . getIndexOfNap ( nap , i ) . getEnergia () ; lista [2] += Adatok . getIndexOfNap ( nap , i ) . getSzenhidrat () ; 32
3.4. Osztályok bemutatása lista [3] += Adatok . getIndexOfNap ( nap , i ) . getZsir () ; lista [4] += Adatok . getIndexOfNap ( nap , i ) . getFeherje () ; lista [5] += Adatok . getIndexOfNap ( nap , i ) . getRost () ; } TaplalekSzamolt osszesitett = new TaplalekSzamolt (0 , null , null , false , lista [0] , lista [1] , lista [2] , lista [3] , lista [4] , lista [5] ,1.0 , null ) ; showAdatok ( osszesitett ) ; A showAdatok() metódus valósítja meg a felhasználói felület TextView-jainak a módosítását, ezáltal a felhasználó láthatja a bevitt tápértékeket. Az InfoDialog a kereső activitásban levő táplálék információját mutatja, azt, hogy egységnyi táplálékban mennyi tápérték található. A TaplalekHozzaadoActivity feladata a táplálék hozzáadó aktivitás megvalósítása. Ebben az osztályban megtalálható egy EditText melyben megtalálható a táplálék neve, egy Spinner melyben megadhatjuk a táplálék kategóriáját, utolsónak pedig egy ListView melyben a hozzávalókat tároljuk. Ebből látszik, hogy ezen osztályhoz is tartozik egy adapter osztály melynek neve AlapanyagAdapter ennek működése hasonló a SearchAdapteréhez képest, annyi különbséggel hogy más összetételű View-t hoz létre. Ezen View xml fájlja a következőképpen néz ki: < FrameLayout ... > < TextView android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / nev " / > < TextView android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / mennyisegText " / > < LinearLayout android : orientation = " horizontal " android : layout_width = " wrap_content " android : layout_height = " wrap_content " > < ImageView android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / torles_button " android : background = " @drawable / ic_ rem o ve_ circle_outline_bla ck_48dp " /> < ImageView android : layout_width = " wrap_content " android : layout_height = " wrap_content " android : id = " @ + id / info_button " android : background = " @drawable / ic_info_outline_black_48dp " / > LinearLayout > FrameLayout > 33
3.4. Osztályok bemutatása Az ebben az osztályban levő Hozzáad gomb megnyomásával a SearchActivity egy olyan filterezett listája nyílik meg a felhasználó számára melyben csak az alapanyagok látszódnak.
3.4.2. Adattárolás és egyéb funkciók osztályainak megvalósítása
Alkalmazásomnak ezen részének fő osztálya az Adatok osztály, melynek feladata az adatok összefogása. A taplaleklista adattag tartalmazza a fájlból már java osztályokba betöltött permamnens táplálékok listáját. A listaNaplo tartalmazza a naplóban levő táplálékokat. A userTaplalekLista a felhasználó által hozzáadott alapanyagokból összerakott táplálékok listáját tartalmazza. Az archiveNaplo adattag a jelenleg megnyitott arhív hét adatait tartalmazza. A resetArchiveNaplo() metódus a fájlból már betöltött és megjelenített arhív naplót törli. Erre akkor kerül sor ha a felhasználó visszalép a kiválaszott hét arhivált aktivitásából a fő aktivitáshoz. Ez csupán memóriatakarékossági célokat szolgál. A resetNaplo() metódus törli a naplóban levő adatokat. A changeTaplalekNaplo() metódus módosítja a már a naplóhoz adott táplálékokat. Ez akkor kerül meghívásra ha a felhasználó utólag módosít a táplálék mennyiségén.
34
3.4. Osztályok bemutatása Az AdatokNaplo osztály tartalmazza a napló adatait naponként listába szedve. Ezen osztályhoz szorosan kapcsolódik a Napok enum típusú osztály. Ezen osztály segítségével ha valamit módosítunk a naplóban az Adatok osztályban egyszerűbb módon tudjuk megtenni, mégpedig kiválasztjuk a kért metódust és annak első paraméterében megadjuk a napot. Ekkor a vezérlés tovább adódik az AdatokNaplo osztályra ahol lefut a hasonló nevű metódus, viszont itt már a kért nap adatait módosítja, vagy adja vissza attól függően hogy az AdatokNaplo osztályban milyen metódust hívtunk meg. A nextNap() metódus feladata visszaadni a következő napot a hétben. Ezen metódus bevezetésével lényegesebben könnyebbé tehető a napló úgymond körbejárása. Ilyen körbejárásra például a resetNaplo() metódusban van szükség. Ezen metódus forráskódja a következő: public static Napok nextNap ( Napok nap ) { if ( nap == HETFO ) return KEDD ; else if ( nap == KEDD ) return SZERDA ; else if ( nap == SZERDA ) return CSUTORTOK ; else if ( nap == CSUTORTOK ) return PENTEK ; else if ( nap == PENTEK ) return SZOMBAT ; else if ( nap == SZOMBAT ) return VASARNAP ; return null ; } A JsonOperator osztály feladata a .json fájlokból való kiolvasás, illetve a mentés folyamán felépíteni a json fájl struktúráját, majd annak kimentése a telefon tárhelyére. Adattagjában tárolja a beolvasott JSONObject-eket. Kimentésnél szorosan összedolgozik az Adatok osztállyal, mivel a kimenteni való adatokat innen kéri le. A következő kódrészlet a permanens tápláléklista json fájlját olvassa be: try { String json = null ; InputStream is = mainActivity . getAssets () . open ( " data . json " ) ; int size = is . available () ; byte [] buffer = new byte [ size ]; is . read ( buffer ) ; is . close () ; json = new String ( buffer , " UTF -8 " ) ; data = new JSONObject ( json ) ; } catch ( IOException e ) { e . printStackTrace () ; } catch ( JSONException e ) { e . printStackTrace () ; } Az alább látható kódrészlet felépíti a napló adatait tároló JSONObject-et: 35
3.5. Tesztelés try { JSONObject vegso = new JSONObject () ; Napok curNap = Napok . HETFO ; for ( int i =0; i <7; i ++) { JSONArray array = new JSONArray () ; for ( int j =0; j < Adatok . getSizeNap ( curNap ) ; j ++) { JSONObject obj = new JSONObject () ; obj . put ( " id " , Adatok . getIndexOfNap ( curNap , j ) . getId () ) ; obj . put ( " mennyiseg " , Adatok . getIndexOfNap ( curNap , j ) . getMennyiseg () ) ; array . put ( obj ) ; } vegso . put ( curNap . toString () , array ) ; curNap = Napok . nextNap ( curNap ) ; } userData = new JSONObject () ; userData . put ( " userData " , vegso ) ; } catch ( JSONException e ) { e . printStackTrace () ; } Az AdatLoader osztály nevéből is látszik hogy az adatokat tölti be a JsonOperatorban tárolt JSONObject típusokból a Taplalek és TaplalekSzamolt típusú java osztályokba. A Taplalek osztály fő feladata a táplálékok tárolása, ezen osztályból több példány is létezhet a program futása során. A TaplalekSzamolt osztály a Taplalek osztály leszármazotta. Ezen típusú osztály található közvetlenül a naplóban. Tartalmazza mindazon adatokat mint a Taplalek osztály kibővítve a mennyiséggel melyet a felhasználó megadott. A TaplalekSzamolt osztály konstruktorában a kapott táplálék és mennyiség függvényében számolja ki a táplálék összetevőinek a mennyiségét. Az AdatokUjTaplalek osztály segíti a felhasználói felületben megtalálható TaplalekHozzaadoActivity-t. Ezen osztály tárolja az aktivitásban megadott új táplálékok információit. Ezáltal ha a felhasználó félbe hagyja a táplálék hozzáadását ezen osztályba letárolásra kerül az előrehaladása, így később folytatni tudja a hozzáadást. Ezen osztály tartalma törlésre kerül, ha a felhasználó végzett az új táplálék hozzáadásával.
3.5. Tesztelés Az alkalmazást a fejlesztés közben teszteltem a saját telefonomon folyamatosan. Az alábbi eseteket vizsgáltam: • A betöltött adatok a kereső listában helyesen jelennek-e meg • A naplóhoz hozzáadott táplálékok helyesen kerülnek-e lementésre, illetve az alkalmazás indulásakor helyesen töltődnek-e vissza • A naplóban levő táplálékok mennyiségének módosítása helyesen megy-e végbe, a tervezettnek megfelelően jelenik-e meg 36
3.6. Felhasználói felületek • A kereső listából kiválasztás után a felhasználó nem adhat meg 0, vagy üres értékű mennyiséget • Napi információkat tartalmazó dialógusablak helyesen jelenik meg, illetve a valós adatokat mutatja • Törlés funkció helyesen működik-e • Új táplálék hozzáadásakor a meglévőekhez a véglegesítés előtt helyesen megy-e végbe az adatok helyességének ellenőrzése (van-e név megadva, kategória kiválasztva, legalább kettő alapanyag hozzáadva) • Új tápláléknak alapanyag hozzáadásánál a betöltődő keresőlista csak az alapanyagokat tartalmazza-e • Táplálék névszerinti keresése helyesen működik-e • Hét archiválásakor helyesen fut-e le a napló nem ürességét ellenörző metódus • Hét archiválása után a jelenlegi hét törlésre kerül-e a naplóból • Archivált hetek mutatásánál üres archívum esetén jelzi-e a felhasználónak azt • A kiválasztott hét után helyesen jelenik-e meg az archivált hét listája Az alkalmazás elkészültekor további kettő Androidos telefonon kipróbálásra került, egy régebbi verziós Androidon és egy legújabbon. Mindkettőn azonosan jelent meg és azonosan helyesen működött.
37
3.6. Felhasználói felületek
3.6. Felhasználói felületek 3.6.1. Főképernyő
Az ábrán számokkal jelöltem a lehetséges felhasználói interakciókat: 1-es számmal a menü hozható elő, ahonnan a további funkciók érhetőek el, 2-es számmal a kiválasztott naphoz nyitja meg a táplálék keresőt, 3-as számmal jelölttel pedig megtekinthetjük a napi táplálék összetételét. 4-es számmal törölhetjük a kívánt táplálékot a naplóból. A naplóban levő ételek képeire hosszan nyomva, a felhasználó megtekintheti és módosíthatja is a hozzáadott táplálékhoz tartozó mennyiségeket, ezenfelül pedig megtekintheti a táplálékhoz tartozó összetételt, a megadott mennyisséggel számolva természetesen.
38
3.6. Felhasználói felületek
3.6.2. Menü
A menü a legkönnyebben az alkalmazás eszköztárában (felső sáv mely tartalmazza a nevét az alkalmazásnak illetve egyéb gombokat is tartalmazhat) található ikonnal érhető el, illetve a képernyő bal oldalától jobb irányba való húzó gesztus hatására is hozzáférhető. Tartalmazza az alkalmazás emblémáját, illetve innen érhetőek el a legfontosabb funkciók, sorban: új táplálék felvitele, táplálékok böngészése (amely a táplálék hozzáadásakor megjelenő keresés ablak átdolgozott verziója), archiválhatjuk a jelenlegi hetet, illetve megtekinthetjük a már meglévő heteket is.
39
3.6. Felhasználói felületek
3.6.3. Keresés
Ebben az ablakban kereshet a felhasználó a táplálék nevének megadásával a listában. Itt található meg a közel 300 beépített táplálék is, illetve a felhasználó által létrehozott táplálékok is itt jelennek meg. Minden egyes táplálék mellett megtalálható egy I betű melyet lenyomva megtekinthető egy illusztráció a táplálékról, illetve egységnyi összetétele. A listában a kívánt táplálék nevére kattintva a felhasználó tovább jut egy másik ablakra ahol megadhatja a kívánt mennyiséget, ezután pedig visszajutunk az alkalmazás főképernyőjébe hozzáadva a táplálékot.
40
3.6. Felhasználói felületek
3.6.4. Táplálék hozzáadó
Itt a felhasználó új táplálékokat tud hozzáadni a keresőhöz. Megadja a nevét illetve a kategóriáját az ételnek ezek után pedig mintha csak egy recepteskönyv hozzávaló listáját néznénk hozzáadhatjuk az alapanyagokat. A véglegesítés gombra kattintva, ha minden szükséges adatot megadtunk előjön egy véglegesítő ablak, ahol a felhasználó megtekintheti az összesített tápértékeket. Ha ezen a lapon mindent megfelelőnek talál jóváhagyás után bekerül az adatbázisba a táplálék. Ezen ablak fontos tulajdonsága, ha a felhasználó elkezdte szerkeszteni az új táplálék adatait és mielőtt befejezné másik ablakra lép, akkor az előrehaladása lementésre kerül, így visszatéréskor ott folytathatja ahol elkezdte.
41
3.7. Továbbfejlesztési tervek
3.6.5. Arhívum
Ebben az ablakban megtekintehetjük a kívánt héten a fogyasztott táplálékokat, napokra bontva.
3.7. Továbbfejlesztési tervek Ezen fejezetben az alkalmazás jövőjét írom le, milyen új funkciók kerülhetnének be a későbbiekben, illetve a meglévő funkciókat miben lehetne felhasználói barátabbá tenni. Elsősorban az alkalmazást tablet-baráttá kell alakítani. A jelen kinézet teljes mértékben mobiltelefonokra van optimalizálva. Ez abban nyilvánul meg hogy egyszerre csak egy ablak tekinthető meg. Ezt tabletes kinézetben kilehet bővíteni úgy, hogy a kijelző egyik oldalán folyamatosan a napló jelenjen meg, míg a másik oldalon felhasználói interakciótól függően a keresőlista, vagy az új táplálék hozzáadó ablak. Ezt úgy lehet kivitelezni, hogy a resources mappában többféle layout mappákat definiálunk mint például layout-large mely jelzi hogy ezen elrendezések tabletre vannak szabva. Az alkalmazás napló kinézetét is át kell szabni, mivel a jelenlegi nagyon zsúfolt lehet főleg kisebb kijelzőjű telefonokon. Telefonon egyszerre csak egy nap kijelzését kellene alkalmazni, míg oldalra lapozással lehetne megjeleníteni a többi napokat. A napokon belül nem egymás mellé hanem egymás alá kerülnének a táplálékok, balra kerülnének a listában a táplálék képei és tőle jobbra pedig az összetevőinek az információi. Plusz funkcióként bekerülhetne az értesítések küldése az Android értesítési listájára. Értesítést lehet küldeni arról, ha a felhasználó előzőleg beállítja az étkezések idő42
3.7. Továbbfejlesztési tervek pontját, akkor a meghatározott időpontokba értesítene hogy adja hozzá a táplálékot a naplóhoz. Értesítést küldhetne még a hét végéről is, melyet úgyszintén a felhasználó állíthat be mely nap legyen az. Ekkor értesítene az alkalmazás arról, hogy az elmúlt hét automatikusan archiválásra került. Értesítések küldésének legkönnyebb módja a szervízekből való küldés, ezáltal implementálni kellene egy Service osztályból származó osztályt amely a rendszer működésével egy időben létezne és működése ezáltal független lenne attól, hogy az alkalmazás el van-e indítva vagy sem. További teendő lenne az alkalmazás Play Store-ba való feltöltése ezáltal biztosítva lenne a széles körben való elterjedése.
43
4. fejezet Összefoglalás Alkalmazásom tervezése során fő célkitűzés volt, hogy egy egyszerűen kezelhető minél nagyobb körben használható étkezési naptár nyílvántartó alkalmazást készítsek. Ezáltal a felhasználója könnyedén figyelhet a szabályos és egészséges táplálkozásra. Eddig még nem terveztem és fejlesztettem ennyire nagy volumenű programot, ezért a tervezés és a fejlesztés kellő kihívással rendelkezett. Egyes részei a tervezésnek nagyobb nehézséget okoztak, mint például a felhasználó-barát felhasználói felület megtervezése. A könnyebbik része a felhasználói felület implementálása és annak működésére irányuló modulok implementálása tűnt. A fejlesztés során egyre jobban éreztem a fejlődést, a hozzáértés növekedését az Androidos programozáshoz. A Miskolci Egyetemen és más egyetemeken sem jellemző az Androidos programozás oktatása, ezáltal szert tehettem olyan kiegészítő tudásra, mely nagyban támaszkodik az egyetemen tanult Java fejlesztésre. Rengeteget tanultam az Androidos alkalmazások működéseiről és a Java-fejlesztési tudásomat is gyarapíthattam a kivitelezés során. Az alkalmazás végeredménye tükrözi a sok elolvasott Androidos fejlesztéshez kapcsolódó leírásokat és dokumentációkat. Az alkalmazással kapcsolatosan kitűzött elvárásokat sikerült kiviteleznem.
44
Irodalomjegyzék [1] http://www.idc.com/prodserv/smartphone-os-market-share.jsp [2] https://www.android.com/history/ [3] https://developer.android.com/index.html
45
Adathordozó használati útmutató • étkezésinapló_projekt.zip: Az Android Studio-s teljes projekt melyben megtalálhatóak a program forráskódjai. (Használatához minimum Android Studio 1.5.1-es verzió szükséges) • etkezesinaplo.apk: Androidos környezetben futtatható telepítőfájl, ami feltelepíti az alkalmazást (Telepítéshez fel kell másolni a fájlt a telefon memóriájára, a beállításoknál engedélyezni kell az ismeretlen forrásokból származó alkalmazások telepítését és csomagtelepítővel futtatni az apk fájlt. Az alkalmazáshoz szükséges minimális 4.0-s verziója az Androidnak) • szakdolgozat_tex.zip: A szakdolgozat pdf fájljának LaTeX-es forrása • szakdolgozat.pdf: A szakdolgozat pdf formátumban • teszt_futtatás.mp4: Rövid videó mely bemutatja a program futását és funkcióit
46