5. lecke Közelségérzékelő szenzor használata

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:

Közelségérzékelő

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.

Projekt importálása

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).

Layout – felhasználói felület

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:

  1. Nyissuk meg az app -> res -> layout -> activity_kozelseg.xml fájlt, figyeljünk rá, hogy alul a “Design” fül legyen kiválasztva.
  2. A baloldali palettáról húzzunk be a képernyőre egy TextView komponenst (a Widget kategóriából), és igazítsuk pontosan közére (függőlegesen, és vízszintesen is)
  3. Írjuk át text tulajdonságát valami értelmesre (pl.: "TÁVOLSÁG"). (Ez csak addig fog látszani, amíg az első adat meg nem érkezik az érzékelőtől.)
  4. A textSize tulajdonsággal állítsuk át a szöveg méretét valami jól láthatóra (pl.: 40sp).
  5. Az id tulajdonsága legyen 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

  6. Ezen kívül tetszőlegesen lehet módosítgatni a már ismert tulajdonságokat! :)

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!

Kitérő – Szenzorok használata

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?

  • Kezdésként nem árt, ha van egy címünk/postaládánk, ahová az újságot kézbesíteni lehet
  • Meg kell keresnünk a postahivatalt.
  • Ha ez megvan, akkor elmehetünk a postahivatalra, és feliratkozhatunk az áhított Zöld Droid magazinra.
  • Kész is vagyunk, csak győzzük olvasni a sok magazin példányunkat!
  • Még azt érdemes megemlíteni, hogy le is illik iratkozni a postahivatalnál, ha már nem vagyunk kíváncsiak a magazinra.

Szóval az Android rendszer érzékelői is ehhez hasonlóan működnek.

  • El kell érnünk, hogy a 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.
  • Meg kell keresnünk és el kell tárolnunk egy változóban a rendszer SensorManager-ét. Ez a Manager osztály itt a postahivatal, akinél fel- és le lehet iratkozni a mérésekre.
  • Jeleznünk kell a 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.
  • Ha a programunk végzett, akkor iratkozzunk le az összes üzenetről.

Java program megírása

1. feladat - Felkészülés az értékek fogadására

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.

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:

  1. A már meglévő függvényeink záró kapcsos zárójele után hagyjunk ki egy sort, majd kezdjük el beírni az új függvényünk nevét TODO 1.2 (mondjuk először legyen onSensorChanged).
  2. Ha az első 4-5 karakter leütése után felnézünk a képernyőre, láthatjuk, hogy megjelent egy kis ablak, benne egy csomó olyan függvénnyel, aminek a nevében szerepel valahol az 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.
  3. Győződjünk meg róla, hogy az van kijelölve (ha nem, a fel-le nyilakkal jelöljük ki!), majd nyomjunk ENTER-t! Ekkor az Android Studio megírja helyettünk! (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!

2. feladat - Feliratkozás az adatokra

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.

2.1. feladat

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!

  1. 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).

  2. 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

  3. Ebben a függvényben rendeld hozzá a sensorManager változóhoz a SENSOR_SERVICE szolgáltatást.

    sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

2.2. feladat

Iratkozzunk fel a szenzortól érkező adatokra, hogy valóban meg is kapjuk azokat!

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!)

  1. Hozzuk létre az alkalmazásunk onResume függvényét. Ezt a legegyszerűbben úgy tudod megtenni, ahogy az onSensorChanged függvényt is létrehoztuk: kihasználva az AndroidStudio segítségét!
    • Kezd el begépelni az onResume szó első néhány betűjét (oda, ahol a függvényt szeretnéd, hogy létrejöjjön)!
    • Válaszd ki a felugró listából az onResume függvényt
    • Enter
  2. A függvény törzsébe a már meglévő supersuper 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 );
  3. Ez alá írjuk meg a feliratkozást elvégző kódot:
    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:

    • ki szeretne feliratkozni (this – azaz ez az alkalmazás)
    • melyik szenzorra (proximity – amit az imént kerestünk meg)
    • milyen sűrűn kérjük az adatokat. (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)

3. feladat - Az adatok feldolgozása

Í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.)

3.1 feladat

  1. Keressük meg az onSensorChanged függvényünket.
  2. A törzsében deklaráljunk egy új változót, aminek a típusa 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.
  3. A változónak adjuk értékül a mért adatot! Ezt az 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]
Nem sikerült követni a fentieket?


            

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.

3.2. feladat

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?

  • Ahhoz, hogy ellenőrizd, két érték egyenlő-e, használd a == műveletet (pl. dolgozatJegy == 5)
  • A kijelzo azonosítójú TextView komponenshez még nincs változó létrehozva a kódban. Ezt a TODO 3.2a és 3.2b sorok helyén pótold az előző leckék alapján! Tudod: változó deklarálása, majd findViewById-vel hozzárendelés

4. feladat - leiratkozás

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!

  1. Hozzuk létre az alkalmazás 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.
  2. A függvény törzsébe a 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!

Szorgalmi feladatok

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.

1* Képek

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

  1. Helyezz el egy ImageView komponenst a layout szerkesztővel.
  2. Állítsd be a layout:width és layout:height tulajdonságait úgy, hogy a teljes képernyőt betöltse!
  3. Állítsd be az id tulajdonságát mondjuk erre: kep
  4. A Java kódban hozz létre egy változót az ImageView-nak
  5. Az 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.
    A függvény paramétere a megjeleníthető kép resource. Ezek azonosítói hasonlítanak a layout komponensekére:
    • R.drawable.szorgmaths
    • R.drawable.szorgcat

2* Háttér színének állítása

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!

  1. Az egész felhasználó felületnek van egy összefogó (gyökér) komponense, ez a layout nevű komponens, amely RelativeLayout típusú. Nézd meg a ComponentTree-t
  2. Ennek a RelativeLayout-nak szeretnénk a színét átállítani. Ehhez azonban szükségünk lesz az id tulajdonságára.
  3. A java kódban hozz létre egy változót a layoutnak, és az 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.
    • Mi lehet a zöld?
  4. Futtasd a programot!

3* Rezgés

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!

Haladó szorgalmi feladatok

4* Adatelemzés

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:

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.