Online Android szakkör (DKRMG)
Az okostelefonok már a kezdetek óta tele vannak mindenféle érzékelővel (szenzorral). Figyelik a fényviszonyokat, mérik a gyorsulást, érzékelik a Föld mágneses terét, valamint érzékelik, ha valami közel kerül hozzájuk. Ez utóbbi felelős például azért, hogy amikor telefonálás közben a füledhez teszed a telefont, kikapcsoljon a képernyő és ne tudd elnyomkodni a füleddel.
Ebben a leckében ezt a szenzort fogjuk mi is felhasználni, és a segítségével készítünk egy egyszerű távolságmérőt.
A kész alkalmazás valami ilyesmi lesz:
A közelségérzékelő (angolul PROXIMITY Sensor) egy egyszerű kis szenzor, ami többnyire a telefonunk tetején található. Az érzékelő működése egyáltalán nem bonyolult. Ha a legközelebb lévő tárgy (pl. az ujjunk vagy a fülünk)…
Ezt a viselkedést küszöbölésnek is hívják. A képen is láthatod, hogy a közeli mérések mind 0 értéket mutatnak, a távoli mérések pedig mind 5-öt mutatnak.
Tehát azt sosem fogjuk pontosan tudni, hogy milyen messze van a legközelebbi tárgy, csak azt, hogy közel van, vagy távol.
Az előzőekhez hasonlóan ebben a leckében is egy előkészített projektben fogunk dolgozni, hogy a még nem szükséges dolgok ne zavarjanak, ellenben a már jó ismert TODO sorok segítsenek minket a lecke során.
Ezen a linken találjátok L05-SensorIntro.zip. Ezt töltsétek le, csomagoljátok ki az eddigi projektek mellé, majd importáljátok be ezt a projektet az Android Studioba. (Ha elakadtok, kövessétek a 3. leckében leírt útmutatót).
Készítsük el az alkalmazásunk kinézetét!
Emlékeztek még mennyi komponenst kellett az előző leckékben felpakolni a képernyőre? Nos, itt sokkal egyszerűbb dolgunk lesz, hiszen egyetlen feliratra (TextView-ra) van szükségünk:
kijelzo
(Ékezetek nélkül. Emélkszel, hogy mire is jó ez a tulajdonság? ).
Az id azonosító alapján tudunk a java kódból később a komponenseinkre hivatkozni. Pl. R.id.ezEgyId
Ezzel a felhasználói felülettel készen is vagyunk! Azért a biztonság kedvéért nézzük meg, hogy tényleg úgy néz-e ki, ahogy szeretnénk: futtassuk az alkalmazást!
A telefonunkban rengeteg érzékelő (szenzor) van. Ezeknek az eszközöknek nagyon különböző működésük van. Gondoljunk például a GPS-re, ami körülbelül 5 másodpercenként meg tudja mérni a helyzetünket elég pontosan, ezzel szemben a hőmérő egy elég pontatlan eszköz, és elég ritkán méregetjük, viszont a telefon forgatását mérő giroszkóp állapotára a másodperc töredéke alatt szeretnénk reagálni. És persze itt van a közelségérzékelő is, amit ebben a leckében fogunk használni: egy elég pontatlan szerkezet, de viszonylag gyorsan kell reagálnia.
Szükség van egy egységes rendszerre, ez a rendszer pedig a feliratkozásos rendszer. A programunk feliratkozik egy tetszőleges érzékelő üzeneteire/méréseire
Hasonlatként nézzük meg, hogy miként működik a feliratkozás a való életben. Tegyük fel például, hogy szeretnénk feliratkozni a nagyon menő Zöld Droid magazinra, amit a helyi postahivatalnál érhető el. Mi történne?
Szóval az Android rendszer érzékelői is ehhez hasonlóan működnek.
KozelsegActivity
-nk úgy is tudjon viselkedni/kinézni,
mint egy postaláda, avagy rendelkezzen egy postaládával, és így fogadhasson érzékeléseket/üzeneteket.SensorManager
-ét.
Ez a Manager osztály itt a postahivatal, akinél fel- és le lehet iratkozni a mérésekre.SensorManager
-nél, hogy innentől kezdve szeretnénk értesülni minden olyan adatról,
amit a közelségérzékelő rögzített.Az érzékelő által közölt értékeket a KozelsegActivitynk fogja fogadni.
Ehhez azonban néhány apróbb átalakítást kell rajta elvégeznünk.
Egészen pontosan azt kell elérnünk, hogy kívülről úgy nézzünk ki, mint egy postaláda,
azaz egy SensorEventListener
. Ehhez módosítanunk kell az osztályunk nevének sorát,
illetve meg kell írnunk a SensorEventListener
két függvényét is.
SensorEventListener
. SensorEvent-eket akarunk fogadni!
A fájl teteje felé keressük meg azt a sort, ami a public class KozelsegActivity
szavakkal kezdődik,
és a megfelelő helyre írd be a következőt: implements SensorEventListener
TODO 1.1
Nem elég azt mondanunk, hogy mostantól olyanok vagyunk, mint egy SensorEventListener
.
Ilyenkor kötelező megírnunk a SensorEventListener
osztály két nagyon fontos függvényét is:
public void onSensorChanged(SensorEvent event)
public void onAccuracyChanged(Sensor sensor, int accuracy)
Hogyan tudod a lehető legegyszerűben létrehozni ezeket a függvényeket? Ha megírtad az előző lecke logging szorgalmiját, akkor remélhetőleg már tudod is a választ! Ha nem, akkor kövesd az alábbiakat:
onSensorChanged
).onSen
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 onSensorChanged
.
super
sor nincsen, és nem is
kell, hogy legyen!)Ne felejtsd el ehhez hasonlóan megírni az onAccuracyChanged
függvényt is.
Próbáljuk meg futtatni a programunkat. Ha valahol valami nem működik, vagy hibát ír ki, akkor nézzük meg újra, hogy pontosan követtük-e a leírást! Ha még ezután sem sikerül futtatni, akkor küldd el a java fájlodat emailben, vagy tömörítsd be a projektedet egy zip fájlba, és használd a honlapunk új feltöltő oldalát!
Hurrá! A programunk mostantól jogosult a szenzor adatainak fogadására!
Az 2. feladatban megteremtettük a lehetőséget, hogy az alkalmazásunk megkapja a szenzor által mért adatokat. Most következik a SensorManager megkeresése és feliratkozás az üzenetekre.
SensorManager megkeresése és eltárolása egy változóban
A feladatunk szerencsére nagyon hasonlít az előző leckéhez!
A SensorManager
-t ugyanúgy kereshetjük meg, mint az előző leckében a Vibrator
-t!
Hozz létre egy SensorManager típusú változót (program elején, függvényeken kívül).
Legyen a neve mondjuk sensorManager
(kisbetűvel).
Emlékszel még az előző leckéből, hogy melyik az az életciklus függvény, ami legelőször lefut? Itt kerestük meg a rezgő motort is!
A program futásakor a legelső életciklus függvényünk az onCreate
Talán nézz ide vissza
Ebben a függvényben rendeld hozzá a sensorManager
változóhoz a SENSOR_SERVICE
szolgáltatást.
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Az előző leckében megismerkedtünk az alkalmazásunk életciklusával. Az egyik feladatban elértük, hogy amíg az alkalmazásunk fut, addig rezegjen a telefon. Most ehhez nagyon hasonló lesz a feladatunk: amíg az alkalmazás fut, addig kell "fülelnie" a szenzor adataira, és ha leáll, akkor abba kell hagyni a figyelést.(Ismétlés: melyik két függvényt kell ehhez megírnunk? Ha nem jut eszedbe, olvasd el megint a 4. lecke megfelelő részeit!)
onSensorChanged
függvényt is létrehoztuk: kihasználva az AndroidStudio segítségét!onResume
szó első néhány betűjét (oda, ahol a függvényt szeretnéd, hogy létrejöjjön)!onResume
függvénytsuper
super sor után írjuk be a következő sort,
amivel megkeressük, hogy konkrétan melyik szenzorra szeretnénk feliratkozni:
Sensor proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
Ez talán némi magyarázatra szorulhat:
A sensorManager változónk a rendszer azon központi részére mutat, ami az összes szenzorral kapcsolatos dolgot kezeli.
A fenti sorral lehet kérni a managertől egy adott funkciójú (Sensor.TYPE_PROXIMITY
) változót,
és azt elmenteni egy Sensor típusú, proximity nevű változóba.
A Sensor.TYPE_PROXIMITY
a közelségérzékelő szenzorra utal.
Ez nagyon hasonlít arra, ahogy a felhasználói felület komponenseit lehet megkeresni:
Változó típusa | Változó neve | Változó típusa | Keresést elvégző függvény | Azonosító (mit keresünk) | Sor vége :) | |
---|---|---|---|---|---|---|
Button | gomb1 | = | (Button) | findViewById( | R.id.button | ); |
Sensor | proximity | = | sensorManager.getDefaultSensor( | Sensor.TYPE_PROXIMITY | ); |
sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_UI);
Mazarázat: A sensorManager megfelelő függvényét meghívva tudunk feliratkozni (regisztrálni) a szenzor által küldött adatokra. A függvények három paramétere van:
this
– azaz ez az alkalmazás)proximity
– amit az imént kerestünk meg)SensorManager.SENSOR_DELAY_UI
- átlgos sebesség,
a felhasználói felület frissítéséhez elegendő, de pl. játék vezérléséhez a gyorsabb jobb)Írjuk meg a szenzorból érkező adatot kiolvasó, feldolgozó és megjelenítő kódot!
Most, hogy már megfelelően felkészítettük az alkalmazásunkat arra, hogy fogadja a szenzorból érkező adatokat, és a rendszert is értesítettük arról, hogy szeretnénk megkapni azokat, már csak egy dolog van hátra: valamit kezdeni kéne az adatokkal!
Emlékezzünk vissza, hogy hol érkeznek az adatok? A kapcsolódási ponton keresztül,
ami jelen esetben az onSensorChanged
nevű függvény. Ez a függvény akkor fut le, amikor a szenzor
jelzi a rendszernek, hogy változást érzékelt, és új adatokkal tud szolgálni.
A friss adatok a függvény paraméterében (ami egy SensorEvent
típusú, event
nevű változó)
találhatók.
Nekünk csak annyi a dolgunk, hogy kiolvassuk az event változóból a szenzor által mért értéket, és megírjuk, hogy ez alapján mit csináljon a program (pl. különböző értékekre más-más szöveget írjon ki.)
onSensorChanged
függvényünket.float
, a neve pedig ertek
.
A float
típusú változók nem egész számokat tudnak tárolni (pl.: 0.5
3.14
4.4567
,
Pascal real
típusához hasonlóan). Erre azért van szükség, mert a szenzorok minden esetben ilyen,
nem egész szám formában adják meg a mért értéket.event
változónk values tulajdonságából érjük el.
Honnan van az event
változónk? Ez a függvény paramétere volt, és az összetett
SensorEvent
osztályba tartozik. A values
tulajdonsága pedig egy tömb,
amelynek számunkra az első eleme érdekes. Összességében: event.values[0]
Az event.values egyébként egy float
típusú számokat tároló tömb,
és annak a nulladik elemét olvassuk ki (Igen, a programozók előszeretettel indítják nullától a számozást egy helyett).
Azért egy tömbben érkeznek az adatok, hogy azok a szenzorok,
is használhatóak legyenek, amik egyszerre több mindent mérnek (pl.: giroszkóp, gyorsulásmérő).
Ezzel elértük, hogy a szenzor által mért érték az ertek
változóban van, már csak fel kell dolgoznunk.
A szenzorból megszerzett adat feldolgozása nagyon hasonló a 3. leckében megtanult módszerhez,
amivel a játékos által beírt tippet dolgoztuk fel.
(Ha nem emlékszel az elágazásokra (if(feltetel) { }
), akkor
lapozz gyorsan vissza (3. oldal),
mielőtt tovább olvasnál!
A közelségérzékelő szenzor két féle értéket adhat vissza:
A Java kódunkban ezt könyedén kifejezhetjük:
Ennek megfelelően írj egy if-then-else elágazást a 3.1. feladatban is használt onSensorChanged nevű függvénybe, a 3.1. feladatot folytatva.
HA a szenzor közel állapotot érzékelt,
AKKOR írd ki az 1. feladatban létrehozott TextView komponensre, hogy “Közel van!”,
KÜLÖNBEN írd ki az 1. feladatban létrehozott TextView komponensre, hogy “Távol van!”!
ne feledd: A kijelzo azonosítójú TextView komponenshez még nincs változó létrehozva a kódban
Kell egy kis segítség?
==
műveletet (pl. dolgozatJegy == 5
)Az alkalmazásból kilépéskor/felfüggesztésekor iratkozzunk le a szenzor adatairól!
A 2.2. feladatban az alkalmazás indulásakor feliratkozunk a szenzor adataira. Viszont a leiratkozást még nem írtuk meg, így most ugyan az történik, mint az előző leckében a rezgéssel (csak kevésbé látványos): folyamatosan kéri az alkalmazás a szenzor adatokat, ami nem jó, mert ehhez áram kell, és gyorsabban meríti az akksit.
Az előző leckében már megismert onPause
függvény használatával iratkozzunk le a szenzor adatairól!
onPause
függvényét. Ezt ugyanúgy tehetjük meg, mint ahogyan az onResume
függvényt is megírtuk: az Android Studio segítőkészségét kihasználva.
Tudod, elég ha elkezded beírni a függvényed nevét a megfelelő helyre,
és az Android Studio rögtön felajánlja, hogy befejezi helyetted a kiválasztott függvényt.super
sor után hívjuk meg a sensorManager
változónk
unregisterListener
függvényét. A függvény egyetlen paramétere az, aki le akar iratkozni: this
,
tehát ez az Activity.
Kell egy kis segítség?
sensorManager.unregisterListener(this);
Futtasd a programot!
Ha végeztél, akkor küldd el nekünk a KozelsegActivity.java fájl tartalmát egy emailben, vagy töltsd fel a java fájlt (esetleg az egész projektet tömörítve) a szakkör feltöltő oldalán keresztül!
Most már tudjuk, hogy hogyan iratkozhatunk fel a közelségérzékelő adataira, így rengeteg lehetőségünk van az értékek visszajelzésére.
Ha a közelségérzékelő 0
értéket közöl, akkor a képernyőn jelenjen meg a matek/fizika puskánk.
Egyébként csak egy ártatlannak látszó kép látszódjon!
A projekthez hozzáadtunk két képet is. Ezek között váltogathatsz
layout:width
és layout:height
tulajdonságait úgy,
hogy a teljes képernyőt betöltse!onSensorChanged
függvény megfelelő helyén változtasd meg a megjelenített képet!
Ehhez használd az ImageView
típusú változód setImageResource belső függvényét. R.drawable.szorgmaths
R.drawable.szorgcat
Ha a közelségérzékelő 0 értéket közöl, akkor állítsuk a képernyő hátterének színét pirosra, egyébként legyen zöld!
RelativeLayout
típusú. Nézd meg a ComponentTree-t
RelativeLayout
-nak szeretnénk a színét átállítani. Ehhez azonban szükségünk lesz az
id tulajdonságára.onSensorChanged
függvényünk megfelelő helyén állítsd át a színét a setBackgroundColor
belső függvény segítségével.
Color.RED
a piros szín.Módosítsd a programot úgy, hogy ha közel kerül hozzá valami, kezdjen el rezegni, amikor pedig eltávolítod, akkor hagyja abba!
Ez a feladat talán ijesztően hangzik, de egyáltalán nem bonyolult! Olvass vissza az előző leckébe (elég a cancel függvényig), és próbáld meg a rezgőmotort beépíteni a jelenlegi programba! (Segítség: Közelség érzkelése esetén egy nagyon hosszú (pl. 5 perc) rezgést indíts el, és ne felejtsd el leállítani!
Ennek a feladatnak az elvégzéséhez ismerned kell a tömböket, ciklusokat, illetve a maximum
kiválasztás algoritmust.
Javasoljuk, hogy ha tudod hogyan kell új osztályokat létrehozni, akkor dolgozz a saját osztályodban!
Nem gond, ha még nem látod magad előtt a teljes megoldást, ha van bármi elképzelésed, próbálkozz, kezdd el,
és írj nekünk, ha elakadtál, vagy kérdésed van. Ez már egy igazi program, igazi kihívásokkal, sokat tanulhatsz belőle!
Okostelefonos alkalmazásoknál sokszor az a célunk, hogy rögzítsük és elemezzük a felhasználó viselkedését. Most itt ezt fogjuk tenni!
Indulásként:
System.currentTimeMillis()
függvény visszatérési értékét, ami egy long típusú
egész szám, és a UNIX Epoch,
azaz 1970. jan. 01. óta eltelt milliszekundumok számát tartalmazza. Ehhez tudsz viszonyítani,
pl. megadhatod, hogy az első érzékelés után 2 perccel kezdődik az adott időszak, és 1,5 percig tart.Ezek után egy gombnyomásra analizáljuk az adatot:
Ne feledd, az alkalmazásod futása megáll, ha bezárod, elforgatod a képernyőt. Az adataid minden leállásnál törlődnek. Így nincs nagyon lehetőséged valódi felhasználó aktivitást elemezni. A feladatok viszont így is érdekesek lehetnek, illetve a jövőben készítünk majd olyan szolgáltatásokat, amik képesek a háttérben is futni.