Online Android szakkör (DKRMG)
Ebben a leckében használni fogjuk a telefon rezgő motorját, illetve egy kicsit mélyebben megismerkedünk az androidos alkalmazások működésével.
Az előzőekhez hasonlóan ebben a leckében is egy előkészített projektben fogunk dolgozni. Ezen a linken megtaláljátok az L04-Razor.zip fájlt, ezt töltsétek le, csomagoljátok ki az eddigi projektek mellé.
Majd importáljátok a projektet az Android Studioba (File -> Close Project, majd Import Project). Ha véletlenül elakadnátok, akkor követhetitek az előző lecke (L03) projekt importálásáról szóló részét.
Ha mindent jól csináltál, akkor a programot le is tudod futtatni
().
Az alkalmazásunk layout-ja azonban egyelőre csak egy általunk odacsempészett képet tartalmaz.
Első lépésként készítünk egy egyszerű alkalmazást, ami egy gomb lenyomásakor bekapcsolja a telefon rezgő motorját (avagy Vibrator angolul).
Ehhez:
Mint bizonyára emlékezhettek az előző leckéből, az alkalmazásunk kinézetét a
Layout szerkesztőben tudjuk megszabni. A layout szerkesztőt az
app -> res -> layout -> activity_main.xml
megnyitásával éritek el. Az új gombot húzzátok mondjuk a
képernyő közepére, a kép alá. A felirata legyen: rezegj!
.
A layout:width
tulajdonsága pedig match_parent
, hogy a képernyő teljes szélességét
kihasználja.
Nyugodtan átállíthatjátok a szöveg méretét/színét stb.
Remélhetőleg ismerős az alábbi függvény, amely rezegjOnClick
névre hallgat.
A függvény törzse a komment lesz, ahol perjelek mögött a komment
emlékeztet minket: a függvényünk egyelőre üres.
public void rezegjOnClick(View v) {
// TODO 1.5 Ezt itt még be kell fejezni
}
A függvényt a TODO 1.2 helyére írd be! Figyeld meg, hogy egy szintre került a többi, már meglévő függvénnyel (azaz a sor ugyan annyival kezdődik beljebb, mint a függvények). Ezt nyugodtan meg is jegyezhetjük: a Java nyelvben a függvények mindig egy szinten helyezkednek el.
Ahhoz, hogy a gomb lenyomásakor a függvényünk lefusson (meghívásra kerüljön), át kell állítanunk a gomb "onClick" tulajdonságát a layout editorban az új függvényünkre.
A vibrátor motor eléréséhez létre kell hoznunk egy változót. Előfordulhat, hogy csodálkoztok: Miért is van szükség egy változó létrehozására,
amikor mi csak meg szeretnénk kérni a telefonunkat, hogy rezegjen egy picit?
Gondoljatok így a helyzetre:
A telefon rezgő motorja egy picit olyan, mint egy gomb, amit elhelyeztünk a
layout szerkesztővel, csak ezt a telefon gyártója rakta bele a telefonba.
Mi tudjuk, hogy létezik, de a Java kódunknak ennél több kell: Létre kell hozunk
egy Vibrator
típusú változót, majd hozzá kell rendelnünk a
telefonunkban elhelyezkedő hardverhez. (Emlékezz, a Button
típusú változónkhoz
is hozzárendeltünk egy-egy gombot, megadott id
azonosítóval).
Tehát hozzunk létre egy Vibrator típusú változót TODO 1.4a
Vibrator rezgoMotor;
A hozzárendelést az onCreate
függvény utolsó sorában TODO 1.4b
az alábbi paranccsal tehetjük meg:
rezgoMotor = (Vibrator) getSystemService(VIBRATOR_SERVICE);
Ugye hogy ez mennyire hasonlít ahhoz, amit a gomboknál csináltunk az előző lecke alkalmával?
gomb = (Button) findViewById(R.id.gomb1); // ez a sor csak minta, ne írd be
Ezen a ponton rendelkezünk egy Vibrator
osztályú változóval.
A Vibrator
osztályú változóknak pedig több hasznos belső függvényük is van.
A jövőben az olyan összetett típusokat, amik saját belső függvényekkel
rendelkeznek, sokszor osztályoknak fogjuk hívni. Így különböztetjük meg a
primitív típusokat (egész számok: int
, karakterek: char
,
bool változók: boolean
, lebegőpontos számok: float
vagy double
)
az olyan típusoktól, mint pl. Button
, TextView
, Vibrator
.
Emlékeztetőül: a gombjaink belső függvényeit (mint pl. a setText
)
a következő módon hívhatjuk meg:
<változó>.<függvényNeve>(<paraméterek>);
pl.:
gomb1.setText("új szöveg");
A Vibrator osztály rendelkezik egy igen hasznos függvénnyel:
vibrate(milliszekundum)
A zárójelben lévő paraméter szabja meg, hogy hány ezredmásodpercig rezegjen a telefonunk.
Ezek alapján tudnál írni egy parancsot, ami meghívja/elindítja a rezgoMotor
vibrate
eljárását 1 másodperces paraméterrel (1000ms)?
Írd a parancsot a rezegjOnClick függvényünk törzsébe, hogy a gomb megnyomásakor
induljon el TODO 1.5! Ha nagyon nincs ötleted, akkor kattints a sor végén lévő gombra.
Ugye nem csak lustaságból nézed meg a megoldást?!
Ha minden jól sikerült, akkor most elindíthatod az alkalmazást! A telefonodon ha megnyomod a gombot, a készülék egy másodpercig rezegni fog.
Próbáljuk megnövelni a rezgés idejét! Írd át a kódot, hogy 1000 ms helyett a készülék mondjuk 10000 ms-ig rezegjen!
Itt érdemes egy kicsit elgondolkoznunk. Tíz másodperc sok idő, így előfordulhat, hogy a rezgést időnként még az idő lejárta előtt le akarjuk állítani. Gondolj például arra, amikor a telefonod csörgés közben szokott rezegni. A hívás fogadásakor a rezgés mindig azonnal leáll.
Szerencsére a Vibrator osztály rendelkezik egy hasznos belső függvénnyel a jelenlegi rezgés
leállítására is. Ez pedig a cancel()
függvény. Figyeld meg, hogy itt nincs a függvénynek
paramétere, hiszen rezgés leállításához nincs szükség több információra.
Az előző feladatok alapján hozz létre egy új gombot az előző alá, és érd el,
hogy a megnyomásakor lefusson a rezgoMotor
változó cancel()
függvénye!
Ehhez:
állj!
felirattal)alljOnClick
)alljOnClick
függvényünket hívja megalljOnClick
függvényt, hogy állítsa le a rezgést.
Ha mindent jól csináltál, akkor a programod futtatásakor most egy gombnyomásra elindíthatod vagy megállíthatod a telefonod rezgését. Ha a 10 másodperces időt túl rövidnek találod, nyugodtan átállíthatod mondjuk 5 percre (hány ezredmásodperc van 5 percben?). Végtelenített rezgést is lehet csinálni, de az egy picit bonyolultabb, ezért most nem foglalkozunk vele.
Ezen a ponton kénytelenek vagyunk kicsit jobban belemászni az alkalmazásunk és az activitynk életciklusába.
Miért van erre szükségünk? Erre láthatsz egy egyszerű demonstrációt:
Mindennek, ami változik, van életciklusa. Például nézzünk meg egy tanévet: Szeptemberben elkezdődik a tanítás, hétvégente egy picit pihenhetünk, a nagyobb szünetekben (ősz, tél, tavasz) még a tankönyveket is bavágjuk a sarokba, június végén pedig végleg és visszavonhatatlanul lezárjuk a tanévet.
A fenti probléma megértéséhez meg kell néznünk, hogy mi történik a programunk indításakor, bezárásakor stb.
Az Androidos alkalmazásaink alapvető építőköve az Activity. Az Activity biztosítja a kapcsolatot az Android rendszer és a felhasználók között. Kicsit olyan, mint egy Windows ablak, vagy egy Lazarus Form.
Mi is dolgoztunk már Activitykkel, amikor az alkalmazásunk képernyőjének
kinézetét szerkesztettük a Layout szerkesztővel, illetve amikor a mögötte rejlő Java kódot írtuk
(pl. az onClick függvények). Az activitynk neve Main volt, ezért is hívták a Java
fájlunkat MainActivity.java
-nak, a layoutot pedig activity_main.xml
-nek.
Fontos megérteni, hogy ez a kettő szorosan összefügg: layout nélkül nincs mi
megjelenjen, és a Java kód nélkül nem csinál semmit az alkalmazásunk.
Ez a kettő együtt alkotja a mi MainActivitynket!
Azt is fontos tudnunk, hogy a mi MainActivity ablakunk nem működne rendesen, ha nem venne kölcsön néhány sornyi programot a Google által írt általános, unalmas, üres Activitytől. Egészen pontosan a projektünk létrehozásakor a MainActivity épp úgy viselkedik, mint egy áltagos Activity, de nekünk lehetőségünk van a működését (java) és kinézetét (layout) egy kicsit kibővíteni.
Erre szép példa az onCreate függvényünk, ami eddig mágikus módon minden projektünkben benne volt.
Tehát a lényeg: van pár nagyon fontos függvényünk, amit felhasználhatunk az Android programozás folyamán. Az onCreate az egyik ezek közül, ami mindig a program indításakor fut le. Van azonban néhány hasonló függvény is, ezekkel fogunk nemsokára megismerkedni
Vessünk most egy pillantást a Java kódra (MainActivity.java), és ismerkedjünk meg a kód különböző részeivel.
Kattints a zölddel bekeretezett részekre, hogy megtudd: melyik sor mire való. Baloldalt egy általános magyarázatot találsz, jobboldalon pedig egy program kezdeményt láthatsz az előző leckéből
Kattints rá lent a zöld keretes részekre, hogy megtudd, mire valók!
A package segít a programunk fájljainak rendszerezésében. Ez főként akkor szükséges, ha nagyobb programokat írunk, egyelőre nem kell vele foglalkozni.
Az Android Studio ide sorolja fel, hogy hol találhatók a különböző osztályok, amiket használunk az alkalmazásunkban. Többnyire ezzel sem kell foglalkoznunk.
Ez az activitynk neve. Meg kell, hogy egyezzen a fájl nevével.
Az "extends Activity" rész garantálja, hogy megörököljük a Google által megírt általános Activity osztály összes kódját
A komponenseinket (Button - gombok, TextView - feliratok, stb) érdemes rögön (az osztály elején) deklarálni, bemutatni. Így később könnyebben találjuk meg őket. Ne feledd, ezt Lazarusban nem kellett megtenned., itt viszont szükséges.
Az összes többi változó jöhet ide. Például az előző leckében a gondolt számot, vagy épp a véletlen számot készítő generátort mutattuk itt be.
Ezek a sorok elengedhetetlenek a programunk megfelelő működéséhez. Az első parancsról (super) nemsokára beszélünk majd. A második parancs (setContentView) teljesen lényegtelen, ha véletlenül mégis érdekelne: ez a sor határozza meg, hogy az activity_main.xml layout jelenjen meg az Activity elindulásakor, ha úgy tetszik, ez kapcsolja össze a Java kódot (mit csinál) és a layoutot (hogy néz ki).
Érdemes a változóinkat még az OnCreate függvényben társítani, mert így a program indulásátától kezdve nem kell miattuk többé aggódnunk.
Ha van olyan változónk, aminek a program elején még értéket kell adnunk, azt itt tehetjük meg.
Itt sorakozhatnak egymás alatt a különböző függvényeink.
Az eredeti Activity osztálynak van pár érdekes függvénye, amivel eddig nem is találkoztunk, vagy csak nagyon érintőlegesen használtunk. Ezek olyan függvények, amit az Android rendszer automatikusan indít el adott pillanatban, ezzel jelezve az alkalmazásunknak (vagy épp az Activitynknek), hogy bekövetkezett az adott életciklus eseménye ( → ezért életciklus függvények).
Például nézzük meg a fenti interaktív példán az onCreate
függvényt.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // a super sor minden örökölt függvénynél kell!
// kiegészítés
// ide jön a saját kódunk
}
Az első sor: super.<függvény neve>(<esetleges paraméterek>)
biztosítja,
hogy az a függvény is lefut, amit a Google-nél írtak meg. Ez szükséges ahhoz,
hogy egyáltalán működni tudjon az alkalmazásunk.
Az Activitynk onCreate függvényét akkor futtatja a rendszer, amikor frissen elindítjuk az alkalmazást. Ez a legkorábbi pont, ahol bármit tudunk csinálni az alkalmazás futása során. Érdemes ide rakni a változóink értékadását (pl.: layout komponensek: gombok, TextView-k, stb. illetve egyéb rendszerkomponensek: rezgő motor).
Az elindulás (születés) nagyon fontos esemény, de koránt sem az egyetlen az Activitynk futása (élete) során. Nézzünk meg még kettőt, és rögtön használjuk is fel őket!
Az onPause
függvényt akkor hívja a rendszer, amikor az alkalmazásunk futása valamiért felfüggesztésre
kerül. Ez történik például a programunk bezárásakor (vissza gombbal, vagy a home gombbal).
A TODO 3.1 helyre írd be az alábbi pár sort!
@Override
protected void onPause() {
super.onPause();
// ide írhatjuk a saját kódunkat...
}
Az @Override
sort nem muszáj beírnod. Ez csak egy komment-szerű jelzés, hogy az Activity
egyik függvényét készülünk bővíteni;
ezért nem is találkoztál még vele a saját (pl. gomb kattintás kezelő) függvényeidnél, hiszen azok a
Google által írt "ős-Activityben" nem szerepelnek.
Például írjuk be saját kódként azt a parancsot, ami leállítja a telefon rezgését!
Ha most elindítjuk a programot, mit tapasztalunk a program bezárásakor? Abbamarad a rezgés?
Az onResume
függvényt többnyire akkor hívja a rendszer, amikor az alkalmazás visszatér a
felfüggesztett állapotból, de például lefut az első indulásnál is, az onCreate
után (ld. később).
@Override
protected void onResume() {
super.onResume();
// ide írhatjuk a saját kódunkat...
}
Például írjuk be saját kódként azt a parancsot, ami elindítja a rezgést 5 percig!
Ha most elindítjuk a programot, mit tapasztalunk? Mi történik az első indításkor? És a program menüből történő újraindításánál?
Az alábbi ábra azt mutatja meg, hogy mi minden történik az alkalmazásunkkal élete folyamán. A színes buborékok az Activity lehetséges állapotait (Fut, Felfüggesztve), a nyilak pedig a változásokkor futó függvényeket mutatják.
Egyes események következtében (pl. képernyő lezárása) az alkalmazás állapotot válthat, ilyenkor pedig a megfelelő nyílon lévő függvény(ek) elindul(nak). (Az OnDestroy függvénnyel egyelőre nem kell foglalkoznod, de találkozol vele, ha/amikor megcsinálod az első szorgalmi feladatot. )
Próbáld ki, hogy mi történik / melyik függvény hívódik meg, ha …
A fenti kérdésekre a válaszokat küldd el nekünk emailben (dkrmg.android@gmail.com)!
A fentiek alapján nagyjából rá lehet érezni, hogy mikor melyik életciklus-függvény fut le, de azért ez egy kicsit bizonytalan. Jó lenne pontosan látni, hogy melyik, mikor és milyen sorrendben fut le, nem? A rezgés elindulása és megállítása sok mindent elárult nekünk, de az Android rendszer ennél egy sokkal jobb eszközt is tartalmaz: A LogCat elnevezésű "rendszert", amivel rövid szöveges üzeneteket, értesítéseket tudunk kiíratni úgy, hogy azokat a gépen is láthassuk.
Az ilyen log-üzenetek kiíratása pofon egyszerű. Annyi a dolgunk, hogy a kérdéses helyekre a java kódban beírjuk a következő parancsot:
Log.e("MainActivity", "...ide jöhet egy tetszőleges szöveg, ami meg fog jelenni...");
A fenti utasítás a következőképp épül fel:
Meghívjuk a Log
osztálynak e
nevű belső függvényét.
Az első paraméter ("MainActivity"
) egy azonosító, hogy könnyebben megtaláljuk, hogy mi írta ki az üzenetet,
a második pedig maga az üzenet ("...ide jöhet egy tetszőleges szöveg, ami meg fog jelenni..."
).
Próbáljuk is ki!
A már meglévő életciklus-függvényekbe helyezzünk el egy-egy logoló utasítást, és ellenőrizzük, hogy mikor futnak!
Log.e("MainActivity", "onCreate fut");
Elvileg most már szépen naplózza az alkalmazásunk a saját működését. De hol tudjuk mi ezt a naplót megnézni?
app: hu.dkrmg.android.razor
! Ha nem ez van ott,
akkor próbáljuk meg lenyitni a legördülő listát, és onnan válasszuk ki ezt a sort,
vagy futtassuk újra az alkalmazást!Értelmezzük, hogy mit is látunk!
Ha megnézzük, hogy milyen sorrendben jelennek meg az üzenetek, láthatjuk, hogy milyen sorrendben futnak le a különböző életciklus-függvények.
A Logcat ablak emlékszik a korábbi futtatásokra is, ezeket az üzeneteket futtatás előtt érdemes az üzenetek mellett oldalt található kis kuka gombbal törölni.
A MainActivity fájlban már létezik néhány életciklus függvény:
onCreate
, onResume
, onPause
.
Az ábránkon volt még ezen kívül egy onDestroy
nevű függvény is, ami a kilépéskor fut le.
Honnan tudhatjuk meg, hogy pontosan hogyan kell kinézne (azaz mit kell írni a függvény neve elé, és utána, még a nyitó kapcsos zárójel előtt)?
Erre több lehetőségünk is van, most nézzük meg a legkényelmesebbet.
onDestroy
.onDe
karaktersorozat. Ezek közül már a mi feladatunk kiválasztani a
nekünk szükségest, de ez nem okozhat gondot, hiszen csak egy olyan van, aminek
a neve onDestroy
.
super.onDestroy();
sor is szerepeljen benne.
Ezen a héten a szorgalmi feladat során egy rezgő Morze készüléket készíthettek a telefonotokból.
A Morzekódban a betűket rövid és hosszú jelekké alakítjuk. Ha nem hallottál még a Morzekódról, akkor érdemes elolvasni a Wikipédia cikket.
A feladat: EditText mezőbe beírt szöveget gombnyomásra Morze jellé alakítunk, majd lejátsszuk a telefon rezgő motorjával
Ehhez:
MorseCode
osztály encode
függvénye. Az eredmény típusa long[]
,
ami a Java nyelvben egy tömb. A tartalma miatt nem kell aggódnod, azt a rezgoMotor változód meg fogja érteni.
long[] minta = MorseCode.encode("hello"); //hello szó morze kóddá alakítása
rezgoMotor.vibrate(minta,-1); // -1 jelzi, hogy ne ismételjük a rezgés mintát
A MorseCode
osztály kódját megtalálod a MainActivity.java mellett. Nyisd meg a fájlt, tanulmányozd egy kicsit,
majd próbáld elmagyarázni, hogy miként is működik a MorseCode osztály.