12. lecke Térkép

Online Android szakkör (DKRMG)


Ebben a leckében végre ismét egy olyan programot készítünk, ami kevés kóddal is látványos eredményt ér el! A célunk egy térkép megjelenítése az androidos okostelefonunk kijelzőjén, ami gombnyomásra tetszőleges helyre ugrik.

Joggal kérdezhetnénk: hány száz sor kódot kell megírnunk ahhoz, hogy egy olyan térképet jelenítsünk meg, ami a világ bármely pontjára elrepít minket, követve az ismert ujj mozdulatokat (mozgatás, nagyítás)? Pedig nem lesz szükségünk másra, mint pár layout elemre, és körülbelül tíz sor új Java kódra.

API

A programozás hőskorában nem ment ritkaságszámba az, hogy minden fejlesztő (vagy cég) a legapróbb problémák megoldására is saját kódrészletek írásába kezdett. A Java nyelv kellő ismeretével mi is megtehetnénk ezt, ám kérdés, hogy miért vesztegetnénk az időnket és erőnket már rég megoldott feladatokra. Kissé közhelyesen: ne akarjuk újra feltalálni a spanyolviaszt! Rengeteg népszerű programozási problémára léteznek szabadon elérhető, nyilvános és sokszor ingyenes kódcsomagok, amikhez a fejlesztőjük (vagy fejlesztőcsoportjuk) úgynevezett API-kon keresztül hozzáférést biztosít számunkra.

A helyzet talán nem is teljesen ismeretlen számunkra. Az Android beépített függvénytára már most is rengeteg munkát vesz le a vállunkról (ablakok kirajzolása, Activityk megnyitása, kezelése, szenzorok használata stb.). Ezúttal egy újabb nyilvános API-val ismerkedünk meg, amely kifejezetten térképek megjelenítésére alkalmas.

Miért Open Street Map (OSM) és nem Google Maps?

Több ingyenesen elérhető térkép API is elérhető jelenleg az interneten. Az Open Street Map projekt egy közösségalapú térkép. Az adatokat magánemberek szolgáltatják, akár Te is hozzáadhatsz új elemeket! A projekthez kapcsolódó osmdroid pedig a világtérképet elérhetővé teszi az Android kódunkból is. A Google Maps is nyújt hasonló API-t, azonban ennek használatát a Google szigorúbb ellenőrzés alatt tartja. A Google Maps API első használata előtt be kell szereznünk egy Android Certificate-t és egy Google Maps API keyt. Ezek ugyan ingyenesen elérhetőek, ebben a leckében most úgy döntöttünk, hogy megkíméljük az olvasót ettől a kényelmetlenségtől, és a könnyebb utat választva inkább az Open Street Map térképeit használjuk. Az érdeklődők itt megnézhetik a Google Maps használatát is, az sem ördöngősség...

Fontos megjegyezni, hogy ennek a leckének a sikeres teljesítéséhez (illetve az elkészült program használatához) szükség lesz a telefonon internetelérésre. A program működik mobilneten keresztül is, de nagyon változó, hogy éppen mennyi adatot szeretne letölteni, ami lehet sok, így drága is. Ezért azt javasoljuk, hogy mindenképp az otthoni WiFi-re legyetek felcsatlakozva, miközben használjátok a programot! Reméljük ez nem okoz gondot senkinek.

Külső API hozzáadása

Az Open Street Map használatához először is szükségünk van egy új alkalmazásra. Ezt hozzuk létre most a szokásos módon. Remélhetőleg ez már mindenkinek kisujjból megy, de ha esetleg mégsem (régen volt rá utoljára szükség), akkor pl. a 7. lecke leírásából lehet puskázni.

Ha kész az új projektünk, akkor nyissuk meg a build.gradle fájlunkat.

Ez a fájl mondja meg az Android Studio-nak, hogy miként fordítsa le a programunkat. Épp az imént itt módosítottuk például, hogy milyen SDK-val rendelkező telefonokat támogassunk. Most azonban a figyelmünket fordítsuk inkább a dependecies rész felé.

A gradle fájl dependencies részlete tartalmazza azon kódcsomagok listáját, amik nem részei se az Android rendszernek, se a Java nyelvnek. A lista már most sem feltétlenül üres, az Android Studio esetenként például már hozzáadta az appcompat könyvtárat. Ha a te dependencies listád más elemeket tartalmaz mint a képen láthatóak, akkor sem kell aggódnod. A lényeg az, hogy a lista végére oda kerüljenek az Open Street Map könyvtárai, ezáltal jelezve az Android Studio-nak, hogy mostantól tekintsen úgy a térkép API-ra, mint bármilyen más belső kódra.

Ehhez másoljuk be az alábbi két sort a dependencies blokk végére:

compile 'org.slf4j:slf4j-android:1.6.1-RC1'
compile 'org.osmdroid:osmdroid-android:4.3'

Most pedig kattintsunk a sarokban felugró Sync now feliratra!

Ha valami oknál fogva a Sync now sáv nem ugrik fel magától, akkor sem történik semmi probléma. A gradle fájl változtatása után nyojuk meg a fenti sávban található Sync Project with Gradle Files gombot (lenti képen pirossal keretezve).

Engedélyek

Korábban a rezgőmotor használatakor is láthattuk már, hogy az Android rendszer bizonyos hardverelemek használatát külön engedélyhez köti. Emlékeztetőként nézd meg a gyorssegély lapot!

Az alkalmazásunk a szükséges engedélyek listáját az AndroidManifest.xml fájlban tárolja.

  1. Nyissuk meg a manifeszt fájlt

  2. Adjuk hozzá az OSM projekt által ajánlott engedélyeket az application sor fölé

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    

A fenti engedélyek listája az OSM honlapjáról származik, és közülük néhányra a mi alkalmazásunknak szigorúan véve nem lesz szüksége (pl. FINE_LOCATION). Az Android programozás világában sajnos elfogadott szokás ilyenkor is kikérni az engedélyt amolyan "ártani nem árt" alapon. Azonban komolyabb alkalmazások készítésekor javasolnánk, hogy igyekezz a lehető legkevesebb engedélyre szorítkozni.

Layout – felhasználói felület

Térkép

A térkép megjelenítésére egy ún. MapView típusú komponenst fogunk használni. Hiába nézegetjük azonban a baloldali palettát, ott a szokásos komponenseinken (Button, TextView, EditText, ListView) túl nem találunk semmi újat. A Layout szerkesztő a külső kiegészítő komponenseket a Custom pont alatt rejti el.

  1. Kattintsunk A Custom pont alatt a CustomView-ra.

  2. A felbukkanó ablak kiegészítő komponensek egész hadát rejti. Itt válasszuk ki a MapView (org.osmdroid.views) komponenst, majd a layout szerkesztőben a telefonra kattintva helyezzük el a képernyő közepén. A térkép helyén ilyenkor egy szürke rácsos mintát látunk csak.

  3. Előfordulhat, hogy elsőre a layout szerkesztő még panaszkodik az ismeretlennek tűnő komponens miatt The following classes could not be found.... A probléma megoldható a jobb felső sarokban található frissítés gomb megnyomásával.

  4. Ne felejtsük el a térkép id tulajdonságát beállítani!

Java kód

A felhasználói felület kialakításával már csak egy lépés maradt hátra a térkép megjelenítéséhez: az Activitynk indulásakor be kell állítanunk a térképünk pár fontos tulajdonságát.

Először is:

  1. Hozzunk létre az Activity elején egy új MapView típusú változót (mondjuk terkep néven)

  2. Rendeljük hozzá a terkep változóhoz a térkép komponensünket az onCreate-ben (findViewById).

A térképünk tulajdonságait praktikus módon rögtön az onCreate függvényben be is állíthatjuk:

terkep.setTileSource(TileSourceFactory.MAPNIK); // térkép forrása
terkep.setBuiltInZoomControls(true); //nagyitás/kicsinyítés látszik
terkep.setMultiTouchControls(true); // kétujjas nagyítás/kicsinyítés engedélyezése

Ennyi?

Igen! Indítsd el a programot, és ha pontosan követted a fenti leírást, egy csinos kis világtérképnek kell várnia a telefonod kijelzőjén.

Térkép mozgatása

Induláskor a térképünk jelenleg az egész földet mutatja kilapítva (esetenként több példányban egymás mellett). Manuálisan lehetőségünk van nagyítani és mozgatni a térképet, így megkeresve a számunkra épp érdekes részletet, ám mennyivel hasznosabb lenne, ha a térkép gombnyomásra egy általunk érdekesnek tartott helyre ugrana!

Az alábbi kód alkalmas ennek elérésére:


IMapController mapController = terkep.getController();
mapController.setZoom(17);
GeoPoint startPoint = new GeoPoint(47.632485, 19.131230);
mapController.setCenter(startPoint);

A térképen a pozíciót a földrajzi szélesség és hosszúság segítségével határozhatjuk meg

Azt szeretnénk elérni, hogy a felhasználói felületen legyen 5 gomb, ami mind más-más helyre repít el minket a térképen. Ehhez megtehetnénk, hogy a fenti kódot öt példányban bemásoljuk a gombjaink kattintását kezelő onClick függvényeibe. Másolgatás helyett azonban ezúttal inkább készítünk egy saját függvényt

Kitérő – Mi fán terem a függvény?

A korábbi leckék során már sokszor emlegettük a függvényeket, de úgy igazán nem néztük meg, hogy mik is azok pontosan. Ezt most pótolnánk. Ha úgy érzed, hogy Te már ismered a függvények lelkivilágát, nyugodtan ugord át ezt a részt. Természetesen bármikor vissztérhetsz.
Akik kevésbé magabiztosak a függvényeket illetően, vagy csak Javaul nem ismerik őket, nekik érdemes átolvasni a kattintás után megjelenő kitérőt.

Új függvény

Most készítsünk egy új függvényt az Activitynkben, mondjuk navigalj néven! A navigalj függvénynek két argumentum kell: a szélesség és a hosszúság, ahová menni szeretnénk.


void navigalj(double szelesseg, double hosszusag) {
    //...
}

A függvény törzsét neked kell befejezned a térkép mozgatására vonatkozó kód segítségével.

Függvény hívása

A layouthoz adj hozzá 5 gombot. Ha kell, méretezd át a térkép komponenst, és az alá/fölé rakd le a gombokat. Ne felejtsük el id tulajdonságokkal ellátni a gombokat és a RelativeLayout gyökérelemet sem. Akik megcsinálták a korábbi szorgalmit, amiben a menükezelést mutattuk be, azt is használhatják, és úgy megmaradhat teljes képernyősnek a térkép.

Most készíts minden gombnak egy onClick függvényt. Ehhez segítséget a gyorssegély lapon találsz. Ezek a függvények pedig hívják meg a navigalj függvényt az alábbi paraméterekkel:

  1. gomb: 47.632485, 19.131230

  2. gomb: 47.496122, 19.039881

  3. gomb: 51.500695, -0.124534

  4. gomb: 40.783217, -73.966429

  5. gomb: 48.858225, 2.294386

Futtasd az alkalmazást, és nézd meg, hogy melyik gomb hova visz a térképen! Ennek megfelelően írd át a gombok feliratát!

Finomabb mozgatás

A mapController.setCenter(startPoint); szemmel láthatóan áthelyezi a térkép középpontját. A MapView osztály viszont tartalmaz egy másik függvényt is, ami ezt az ugrás-szerű lépést kicsit finomabbá teszi.

mapController.animateTo(startPoint);

Írd át a programodat, hogy ezt az új függvényt használja a setCenter helyett. Milyen szerencse, hogy megírtuk a navigalj függvényt, így csak egyetlen helyen változik a program kódja (öt helyett).

Az elkészült műveket küldjétek el nekünk a feltöltő oldalon keresztül!