Online Android szakkör (DKRMG)
Az első hét lecke elkészítése után nem túlzás azt mondani, hogy sikeresen vetted az Android programozás talán legnagyobb akadályait. Feltelepítetted az Android Studiot, megírtad az első önálló alkalmazásaidat, saját Activityket hoztál létre, használatba vetted a rezgőmotort és három különböző érzékelőt is. Ehhez meg kellett tanulnod a layout szerkesztő használatát és a Java nyelv alapjait. Gratulálunk!
Az új év (eredetileg januári lecke) viszont most új kihívásokat tartogat. Ahhoz, hogy tényleg otthonosan tudjatok mozogni az androidos alkalmazás fejlesztés világában, kicsit komolyabb programok felé fordítjuk a figyelmünket.
Az egyik fontos változás, hogy nem fogunk minden leckében új programot készíteni; például a mostani leckével egy három leckén átívelő projektbe kezdünk. Ami viszont reményeink szerint nem fog változni: minden lecke végén egy használható alkalmazás lesz a kezetekben, legfeljebb nem lesz teljesen készen.
Az első nagyobb projektben szeretnénk valami olyan programot alkotni, ami hasznosnak bizonyulhat akár a mindennapokban is. Az angolórák/nyelvórák pedig elég fontos részét képezik a mindennapjainknak; ezen belül pedig mi egészen pontosan a szavak megtanulására fogunk összpontosítani.
A mostani leckénk célja nem lesz más, mint egy egyszerű szótárfüzet program megírása. Az applikáció készítése közben megismerkedünk majd a listákkal és azok megjelenítésével, négy Activityvel zsonglőrködünk egyszerre, ezáltal tényleg megtapasztalva az igazi Android fejlesztés nehézségeit. A végeredmény viszont egy teljes, használható applikáció lesz (puskázó appal kombinálva kéretik tanóra keretein belül nem használni!)
Az általunk tervezett program négy Activityből fog állni. Ennek a gyorsan rajzolt (wireframe) terveit itt láthatjátok. Javasoljuk, hogy ismerkedj meg az ábrán szereplő ablakokkal, és vagy másold le, vagy térj vissza ide minden alkalommal, mielőtt munkához látsz. A kép kattintással nagyítható
Ebben a leckében a menü és az About ablakok elkészítése mellett szeretnénk
letenni a SzavakActivity alapjait a szavak kilistázásával. A következő két leckében ezt fogjuk tovább
finomítani, illetve megírjuk a KvizActivity
ablakot is.
Egy szótárfüzet programnál jogosan merül fel a kérdés: honnan jönnek a szavak? Ebben a leckében a szavakat a program készítésekor kell majd beírnunk (például egy tömbbe), részleteket lásd később.
A folytatásban viszont már fájlokat fogunk használni, amiket akár számítógépen is szerkeszthettek, illetve egymással vagy akár osztálytársakkal is megoszthattok. Egy minta fájl különböző nehézségű szavakkal. Ennek kapcsán javasolnánk, hogy mindenki olyan szavakat írjon be (mind a programba, mind a fájlokba), amelyek az órákon szükséges nyelvi tudását tükrözik. Így hasznosabb lesz a program, mint ha a mi fantáziátlan példáinkat használjátok.
Az előző leckéhez hasonlóan most sem készítettünk elő projektet, így azt Nektek kell létrehozni. Kicsit lusták is vagyunk, és nem szeretjük ismételni magunkat, ezért nem írnánk le megint az új projekt létrehozásának menetét, kövessétek az előző leckében leírtakat! Az "Importálás helyett…" rész mind a nyolc pontját figyelmesen csináljátok végig! Amire figyelj:
Alkalmazás adatai
Szotar
C:\DkrmgAndroid\L08-Szotar
)API 19
kiválasztásaMenuActivity
.Ne felejtsd el beállítani a megfelelő SDK verziót, majd futtasd a programot a telefonodon!
Ismétlés a tudás anyja, mondták már sokan, és milyen igazuk van! Ismételjünk most mi is egy kicsit. A 6. lecke (Iránytű) végén létrehoztunk egy második Activityt, ami a készítő nevét tartalmazta. Csináljunk egy ilyen About Activityt ehhez az alkalmazáshoz is. Annak örülnénk a legjobban, ha minél több eszetekbe jutna, de persze ha elakadtok szabad puskázni a 6. lecke leírásából.
A lépések nagy vonalaiban:
public class AboutActivity extends ActionBarActivity {
Ezt javítsd át erre:
public class AboutActivity extends Activity {
Ha nem emlékszel arra, hogy miként tudod megnyitni az új Activityt, akkor puskázhatsz a gyorssegély lapról.
Ehhez a feladathoz tartozik egy szorgalmi is, amiben a gombot felváltja egy felbukkanó menü. Az érdekesség kedvéért javasoljuk, hogy a következő lecke előtt nézd majd meg!
Első megközelítés
Szuper, most már van egy "névjegy" activitynk (AboutActivity) és egy kiinduló menü activitynk, ahonnan az alkalmazás funkciói lesznek elérhetőek. Ideje elkészítenünk az első funkciót, a szavak kilistázását! A cél tehát: egy Activity, ami megjeleníti a program által ismert szavak listáját.
A fentiek mintájára adjunk hozzá a projekthez még egy activityt (puska
itt) SzavakActivity
néven, aminek
a típusa legyen továbbra is Empty Activity!
Figyelj rá, hogy itt is írd át az ActionBarActivityt sima Activityre!
Most, hogy már többször is beírtátok/bemásoltátok az új activityt megnyitó kódot, nézzük meg kicsit közelebbről ezt a két sort!
public void aboutOnClick(View v) {
Intent intent = new Intent(this, ValamiActivity.class);
startActivity(intent);
}
Az első sorban létrehozott Intent
típusú változónk tárolja, hogy melyik Activityt
szeretnénk megnyitni. Írd át úgy a parancsot, hogy SzavakActvityt nyissa meg!
Intent intent = new Intent(this, SzavakActvity.class);
startActivity(intent);
Futólagos ismeretséget köthettünk már az előző leckék során a String
típussal. Emlékeztetésként: ez egy olyan típus, amely szövegeket tárol. Például
nézzük meg az alábbi változót:
String szoveg;
szoveg = "kutya";
A String típus azonban ennél sokkal többre is képes. A szöveg tartalmazhat szóközöket, illetve akár (végre valahára) ékezetes karaktereket is! Például:
String szoveg2;
szoveg2 = "Ez egy szép kis árvíztűrő tükörfúrógép";
Innen jön az ötlet: egy magyar-angol szópárt könnyedén eltárolhatunk egy String típusú változóban:
szo1 = "kutya = dog";
szo2 = "macska = cat";
Azonban a mi programunk remélhetőleg rengeteg szót tárol majd, és mi nem igazán szeretnénk minden szóhoz új változót létrehozni. A megoldás, hogy az előző leckében tanult tömböket alkalmazzuk. Tehát:
A szavainkat egyelőre egy String
típusú tömbben fogjuk tárolni.
Emlékeztetésként, a String tömb egy olyan
adatszerkezet, amelynek minden eleme egy String típusú változó.
szavak
nevű, String tömb (String[]
) típusú
változót a SzavakActvity
-n belül. Az onCreate függvényben pedig töltsük fel adatokkal.
Figyeld meg, hogy a tömbünket első használat előtt a new
szóval létrehozzuk,
majd kapcsos zárójelek között felsoroljuk, hogy kezdetben milyen Stringek legyenek az elemei.szavak = new String[] {"kutya = dog", "macska = cat", "molylepke = moth"};
A fentiek alapján akárhány Stringet (szöveget) megadhatunk kettősidézőjelek ("
)
között, vesszővel elválasztva. Például:
szavak = new String[] { "kutya = dog", "macska = cat", "ló = horse", "tehén = cow", "állat = animal",
"iskola = school", "osztály = class", "osztályterem = classroom", "ceruza = pencil",
"vonalzó = ruler", "gondol = think", "érez = feel", "szeret = like", "ad = give",
"kap = get", "ékesszólás = eloquence", "kétkedő = incredulous", "egyszerű = trivial",
"elkeseredés = exasperation", "győzedelmes = triumphant" };
Vedd elő a saját szótárfüzetedet, és írj is be néhány olyan szót, amit Neked kell megtanulnod!
Az eddigi programjaink során több layout komponenssel is találkoztunk már.
Rengeteg gombot és feliratot hoztunk létre, és a képeket tároló ImageView
sem teljesen ismeretlen számunkra. Ezúttal azonban egy olyan összetett komponensre
lesz szükségünk, ami szavak/szövegek egy egész listáját tudja megjeleníteni. Ehhez
fogjuk használni a ListView
vezérlőt.
A most bemutatásra kerülő ListView
egy elég különös, univerzális elem,
ami kicsit másképp viselkedik, mint a korábbi komponenseink. Ügyelnünk kell arra,
hogy most nem elég csak a layout szerkesztőben kattintgatni ahhoz, hogy String tömbünk
elemei megjelenjenek a listában. A Java kódba is kicsit jobban bele kell másznunk.
Kezdésként helyezzünk el egy ListView komponenst a layout szerkesztőben (Containers kategória alatt). Az Activity legfontosabb feladata a szavak megjelenítése, tehát méretezzük úgy a listánkat, hogy a képernyő nagy részét elfoglalja.
Láthatjuk, hogy a layout szerkesztőnknek fogalma sincs, hogy mit szeretnénk a listánkban megjeleníteni, így jobb híján az "item 1", "item 2", … szavakat használja. Ezeket fogjuk lecserélni a saját szavainkra.
Mielőtt megnézzük a String tömb megjelenítéséhez szükséges mágikus parancsot,
hozzunk létre egy ListView
típusú változót a SzavakActivityben, mondjuk szolista
néven, és rendeljük hozzá a ListView komponensünkhöz (ilyet már sokszor csináltunk a
findViewById
paranccsal)! Ugye adtál id-t a ListView komponensnek?
Tehát jelenleg a szavaink egy String tömbben laknak, és ezeket szeretnénk megjeleníteni a ListView komponensünkön. Nézzük meg az alábbi két sort, majd írjuk be az onCreate függvényünk végére.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, szavak);
szolista.setAdapter(adapter);
Nem kell teljességgel értenünk a fentieket; így is láthatjuk, hogy létrehoztunk egy
átalakítót (adaptert), ami segít a tömbünket a szolista (ListView
) számára megemészthető
formába hozni. Egészen pontosan létrehoztunk egy úgynevezett ArrayAdapter objektumot,
ami szól a ListView változónknak: itt vannak az új adatok a szavak
tömbben,
jelenítsd meg őket!
Ha esetleg kicsit megkavarodtál, most még gyorsan ellenőrizheted, hogy hogyan kéne az onCreate függvénynek kinéznie:
Futtassuk le a programot. Ha minden igaz, akkor a Menüből a megfelelő gombra kattintva eljutunk a Szavak activityre, ahol ott sorakoznak a kódban megadott szavak.
A programunk ugyan szépen működik, gyönyörűen kilistázza a String értékeket. Ha csak ennyit szerettünk volna, akkor most kényelmesen hátradőlhetnénk, elfogyasztva a karácsonyról maradt bejglit (ha még nem száradt ki teljesen).
Ahhoz azonban, hogy továbbhaladjunk, egy kicsit sajnos át kell alakítanunk, hogy miként tároljuk a szavakat.
Emlékeztetésként: egy szópárt (magyar-angol) egy String-ben került tárolásra
"magyar = angol"
formában. Tehát például:
"küklopsz = cyclops", "szem = eye", stb.
Ez egy igen rugalmatlan módszer. Néhány példa, hogy miért nem szeretnénk így tárolni a szópárokat:
=
jelet mondjuk egy nyílra (-->
)A fenti problémára több megoldás is létezik. Korábbi programozási nyelveknél előfordult, hogy például az angol szavakat és a magyar szavakat külön-külön String tömbben tárolták. Mi ehelyett egy Java programozók által kedveltebb megoldást választunk.
A lecke folytatásában betekintés szintjén megismerkedünk a Java nyelvben méltán
népszerű osztály fogalmával. Itt előre szeretnénk leszögezni, hogy ez a
téma túl nagy egy ilyen leckéhez (vagy akár szakkörhöz); a lecke végén a célunk az
lenne, hogy mindenki megértse magát az alap ötletet, és lássa, hogy miként
alkalmazzuk mindezt a szavaink tárolásához. Ne aggódj, ha nem értesz minden részletet.
Emellett szeretnénk megjegezni, hogy Lazarus nyelvből a class
vagy
record
adatszerkezetek ismerete óriási előnyt jelenthet.
Az osztály egy igen érdekes adatszerkezet, ami segítségével létrehozhatjuk a saját változó típusainkat a Java nyelvben. Egészen pontosan amikor megírunk egy osztályt, akkor azzal felvázoljuk
Példaként megnézhetjük a Haromszog osztályt, ami tartalmazza egy háromszög adatait, és felvázolja, hogy milyen "cselekvéseket" végezhetünk egy háromszöggel.
Fontos, hogy mi ezzel egy tervrajzot készítünk csak, azt mondjuk el a Java nyelvnek, hogy egy háromszög típusú változó milyen adatokat tároljon. Nem egy bizonyos háromszögről van szó! Viszont innentől kezdve akárhány Haromszog típusú változót létre tudnánk hozni, amelyek mind a külön-külön a megfelelő adatokat tárolnák, és ki tudnák számolni például a saját kerületüket.
A háromszög osztályt itt és most nem írjuk meg (bár javasoljuk, hogy az érdeklődő kedvűek olvassanak utána a Java osztályoknak és írják meg az osztály kódját). Ehelyett inkább nézzük meg, hogy miként festene egy osztály, ami egy szót tartalmaz, különböző nyelveken!
Tehát a két kérdés, amit fel kell tennünk magunknak:
Szo
típus?A válaszokat most mi adjuk meg:
Vágjunk is bele. Hozz létre egy új osztály Szo
néven!
Most pedig másold be az alábbi kódot a package...
sor alá, felülírva az Android Studio által írt sorokat:
public class Szo {
public String magyar;
public String angol;
@Override
public String toString() {
String sor = magyar + " = " + angol;
return sor;
}
public Szo(String magyar, String angol) {
this.magyar = magyar;
this.angol = angol;
}
public Szo() {}
}
Kattints rá balra a zöld keretes részekre, hogy megtudd, mire valók!
Ez az új osztályunk neve: Szo
. Figyeld meg, hogy az osztály neve
megyegyezik a Java fájléval (Szo.java)
A Szo
típusunknak két adatot kell tárolnia: a szó angol és magyar formáját.
Ezek a Szo
osztály belső változói. De ilyesmit mintha már láttunk volna! Igen,
ez pontosan olyan, mint amit az Activity elején szoktunk csinálni amikor
létrehozunk egy változót valamelyik komponensnek vagy mint a 3. leckében a tippelt
számnak. Tehát az angol és a magyar egy-egy változó, mégpedig String típusú. És mivel
az osztályunkon belül találhatóak, ezért belső változónak fogjuk hívni őket.
A public
szócska felelős azért, hogy írhassunk ilyet: szo1.magyar
Ez a belső függvény való arra, hogy egy Szo
típusú változót majd
szöveggé tudjunk alakítani. Nézd meg, hogy ez a belső függvény hogyan használja a belső változókat!
Ez is ismerős lehet egyébként, egy függvény, mégpedig egy belső függvény,
hiszen ez is az osztályon belül található. Látszik rajta, hogy a neve toString. Java
nyelven ez a toString függvény használható arra, hogy egy osztályt szöveggé alakítsunk.
Ez is egy függvény, ami egészen pontosan abban segít, hogy létre tudjunk
hozni új Szo
típusú változókat. Tehát amikor azt írod, hogy new Szo("macska", "cat")
,
akkor ez a függvény fut le. Az ilyen belső függvényeket konstruktornak hívjuk.
Egyelőre ne fájjon miatta a fejed túlzottan.
Ez is egy konstruktor, vele sem kell egyelőre foglalkozni. Azért hagytuk itt, hogy segítsen később a munkában.
Fontos!
Amint már utaltunk rá, az osztály, amit a fenti kóddal adunk meg, az csak egy tervrajz! Megmondja, hogy hogyan kell kinéznie egy Szo-nak. Ezzel csak egy új típust hoztunk létre. Ahhoz, hogy igazán használni tudjuk, el kell kezdenünk Szo típusú változókat gyártani. Ez a következő kóddal tehetjük meg:
Szo szo1; // változó deklarálása
szo1 = new Szo(); // új, üres szó létrehozása
Ilyenkor létrejön az új változónk. Ha be szeretnénk írni a szo1 változó magyar és angol megfelelőit, akkor azt könnyedén megtehetjük:
szo1.magyar = "kutya";
szo1.angol = "dog";
Láthatjuk, hogy a szo1 mögé helyezett ponttal a szo1 változónk belső változóinak adhatunk értéket (vagy olvashatjuk ki őket).
Mi most még ezen felül csalhatunk is egy kicsit, hogy ne kelljen három sort gépelni, beírhatjuk az alábbit is:
Szo szo1; // változó deklarálása
szo1 = new Szo("kutya", "dog"); // új szó létrehozása
Vagy ha még messzebb akarunk menni, akkor mindez egy sorba is leírható (a konstruktor miatt)!
Szo szo1 = new Szo("kutya", "dog"); // változó deklarálása ÉS új szó létrehozása
Láthattuk, hogy a szo1 változó mögé helyezett ponttal a szo1 változóban lévő Szo típusú objektum
belső változóihoz
nyertünk hozzáférést (pl. szo1.angol, szo1.magyar). Ehhez hasonlatosan nem nehéz
kitalálni, hogy a szo1.toString()
parancs meghívja a szo1 változóban lévő Szo típusú
objektum toString
belső függvényét. Ha megnézed a Szo osztály kódját, akkor láthatod, hogy az az alábbi
Stringet fogja nekünk visszaadni: "kutya = dog". Ha létrehozunk egy macska változót is
(macska =new Szo("macska", "cat");
), akkor a macska.toString()
parancs pedig nem túl meglepő módon a "macska = cat
" Stringet fogja visszaadni.
Eddig nem foglalkoztunk mélyrehatóan a függvények működésével, így nem probléma, ha ezt nem
látod át teljesen.
A szólistánkat eddig egy tömbben tároltuk. Egy olyan tömbben, ami egyszerű Stringekből állt.
Talán nem túl meglepő lépés, hogy most szeretnénk ezt a tömbünket kicsit átalakítani, és egy
olyan tömböt használni inkább, amelynek minden eleme Szo
típusú.
Ha visszaemlékeztek, a lecke elején csináltunk egy tömböt, ami String-eket tudott tárolni,
és ezeket jelenítettük meg a ListView
-ban. Ezt a String tömböt így deklaráltuk
az Activity kódjának elején.
String[] szavak;
Figyeld meg, hogy mi mit jelent/jelez! Először leírjuk, hogy milyen típusú objektumokat
szeretnénk belerakni, majd a []
karakterekkel megmondjuk, hogy nem egyet,
hanem többet szeretnénk tárolni a változóban. Utána már csak a változó nevét
kell megadni (itt pl. "szavak"). Eddig egy változóba csak egy objektum fért, de ha a []
ket
is használjuk, akkor már több is belefér egymás mellé. Ilyenkor tömb-nek hívjuk.
De mi azt szeretnénk, hogy ne String-eket, hanem Szo objektumokat tároljon a tömbünk. Ezt hogyan tudnád elérni?
Szo[] szavak;
Ha a tömbünk típusát átírtuk, akkor a benne lakó adatokat is meg kell változtatni egy csöppet. Az Android Studio most aláhúzza azt a sort, ahol a "macska = cat", "kutya = dog", "molylepke = moth" szópárokat bepakoltuk a tömbbe. Mindezt teljesen jogosan teszi, hiszen a szavak tömbbe most már nem Stringeket, hanem Szo típusú változókat kéne tenni.
Szo szo1 = new Szo("kutya", "dog");
Szo szo2 = new Szo("cica", "cat");
…
szavak = new Szo[]{szo1, szo2, szo3};
szavak = new Szo[] { new Szo("kutya", "dog"), new Szo("macska", "cat"), new Szo("ló", "horse"), new Szo("tehén", "cow"), new Szo("állat", "animal"),
new Szo("iskola", "school"), new Szo("osztály", "class"), new Szo("osztályterem", "classroom"), new Szo("ceruza", "pencil"),
new Szo("vonalzó", "ruler"), new Szo("gondol", "think"), new Szo("érez", "feel"), new Szo("szeret", "like"), new Szo("ad", "give"),
new Szo("kap", "get"), new Szo("ékesszólás", "eloquence"), new Szo("kétkedő", "incredulous"), new Szo("egyszerű", "trivial"),
new Szo("elkeseredés", "exasperation"), new Szo("győzedelmes", "triumphant") };
Már csak annyi feladatunk van, hogy az adaptert is átállítsuk, hogy ne String-eket használjon, hanem Szo típusú változókat. Ehhez keresd meg azt a részt, ahova ezt beírtad (onCreate):
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, szavak);
És ahol String-et látsz, írd át Szo-ra (2 helyen van, ezeket kiemeltük)!
Ezzel készen is vagyunk, ki lehet próbálni a programot! Ha mindent jól csináltál, akkor ugyan azt kell látnod, mint amikor String-ek voltak a tömbben! Mindez annak köszönhető, hogy a ListView (egészen pontosan az adapter) a Szo típusunk kiírásához rögtön a toString() függvény segítéségét kéri. Tehát a ListView minden egyes Szo-nak meghívja a saját toString-jét. És mivel mi úgy írtuk meg a Szo toString függvényét, hogy "angol = magyar" formába alakítsa szöveggé a Szo-t pontosan ezt is fogjuk látni a listánkban.
És hogy mégis miért szenvedtünk ennyit az osztályunk megírásával?
Változtasd meg úgy a Szo osztály toString()
belső függvényét, hogy a
szavak közé egyenlőségjel helyett kötőjelet tegyen inkább!
Futtasd a programot! Minden szónál kötőjel jelenik most már meg?
String sor = magyar + " = " + angol;
Láthatod, hogy a magyar
szót, az =
jelet és az angol
szót hogyan fűzzük
egymás után a + jelekkel. Nincs más dolgod, mint lecserélni itt az egyenlőségjelet egy kötőjelre!
Változtasd meg úgy a Szo osztály toString()
belső függvényét, hogy a nyelveket
angol-magyar sorrendben jelenítse meg!
String sor = angol + " - " + magyar;
Amikor végeztél, töltsd fel a projektedet a szakkör feltöltő oldalán keresztül. Ha elakadtál, vagy kérdésed támad a fentiekkel kapcsolatban, nyugodtan írd meg emailben dkrmg.android@gmail.com
Fentebb említettük, hogy például azért jó, hogy bevezettük az új osztályt, mert könnyebb új nyelvet hozzáadni. Csináljuk is meg! Válassz ki egy tetszőleges nyelvet (mi a németet választottuk), és gondolkozz el, hogy miket kéne csinálni ahhoz, hogy egy Szo objektum a harmadik nyelven is tudja tárolni a szó jelentését! Érdemes lehet visszamenni az interaktív részhez. Ha végiggondoltad, , hogy megjelenjen a megoldás!