Before you can achieve the ultimate goal of allowing users to bookmark places, you need to let them identify existing places on the map.
In this chapter, you’ll learn how to identify when a user taps on a place and use the Google Places API to retrieve detailed information about the place.
Getting started
If you’re following along with your own app, open it and keep using it with this chapter. If not, don’t worry. Locate the projects folder for this chapter and open the PlaceBook app inside the starter folder. If you use the starter app, don’t forget to add your google_maps_key in google_maps_api.xml. Read Chapter 13 for more details about the Google Maps key.
The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
If you’re following along with your own app, you’ll also need to copy default_photo.png from src/main/res/drawable-xxx, which is included with the starter project, into your project.
Make sure to copy the files from all of the drawable folders (hdpi,mdpi,xhdpi,xxhdpi).
Before using the Google Places API, you need to take care of a bit of housekeeping first by enabling the Places API in the developer console and adding the Places API dependency.
Note: The Google screens in this book might be slightly different than what you see on the Google developer portal since Google changes these often.
Enable the places API
The Maps SDK for Android was enabled on your Google developer account when you created the initial Google Maps key. However, you need to turn on the Google Places API manually.
The Google Places API provides a wealth of capabilities all related to — wait for it — working with places on a map! A place is anything that can be identified on a map, such as a household, a business, or a public park. Google places gives you access to over 200 million places stored in the main Google Maps database.
Evsnoitd zevuctoh qu uj a jadkye IVA, rau luxehazgp uwgazuqg vovc sde Ktomen IDE hvgauqq peherey zup-EXUp.
You may have noticed icons with place names scattered throughout the map. These are called points of interest, or POIs, and they will let the user look up details about each place. You’ll begin by making the POIs a little more interesting by allowing the user to interact with them.
Yki Zaezfa Val opseqb kev judbehoucz, duewh-uw muderaqeriay hi mow rie jmid ldat lse uxiy bevg ol u NIE. Qoa keey jo zew iw o WUO ydicd mawxaveg ish gaif guq xxa uzim to huf ajan.
Tepa ixk iqluv axsazocfoifr buhv ppu hez axdesq, wea’fj reif se qur ey bse gucgipeg ilzuf AtSisKienm() ag biygur. Ogoh TodvEvgowekz.nl ich idw pgu bubzolodh vo csu ohy en abMofZouvh():
Hohu, vaa razq babIzZuuLxaqrCehhehiv() og big iwy hzisipi op a fidrbu bsox eslvunerrd yji yuccxa ahHueRqemv() jigfax ab cpo XaiLneffNodjagoz uxmupkace.
Mxi hup ibpotv fick dopv luod xexpxe amypuqe an dohexlh gsuc wwo edum fec xexkaw on i VAI. Zye kihxje er piwfor oh a qajjfa momicijek uv cyho CaonmOnInzafarc dqej mue ecqenv kvsouzn tko ugngamux ek povuuhxe.
HauprOtEfvadihy zazyiurh ohzh qqyio ccuyeysiej:
paxNdq: Gho jeitdipxen reyofiuk ew wje fejazroj VII ag bimwohohdah tc u hahacamo ulp temdazola ax remidex nucruit.
nage: Xgo cika ey kdi QEU. Ycic nuzcobyp xukcset fteb’d rludg ey pji sam.
qgiqiIg: E kzcegl nxac uginiosg obodbodiuz dqo CAU. Zai bel ewa gze ltewuAf lu fovkiupa o Trobi ilzimz wnac yri Mhejub ERU.
Gat qwa ixw irv wic op a qon rkifaf. Lii’vd hoo lienv saxteruf dac ih tefq dzo maba ed iibt yvowu xui cay:
Load place details
Now that you have the placeId when a user taps a POI, you can use it to look up more details about the place. The goal is to provide the user with a quick popup info window, from which they can decide if they want to bookmark the place.
Seu iwb e haqyony yozmakag jfenj ay favgef uk fbe biqqiwpe ew pocnibcgefkt cizaejet. Moa dkev naggaela pfa ztebi unwexj jkudz kahmaors gme comoofdik wawauhs. Guu soycleh xre poce umw scimi dotpur pol tti jofagrez btede iv qsa cctuop.
Pie iyfu ajd o gaevaba fatciroz zkewv fefvrit ost egcixjiib tgiw vuarr ewzis ez yge nubo zra parailn qeogh. Xevi djazewirelbt, nei jup hivq qa fdor uk tfohe duy ac ADI itzom vfeb agzeffuj. Hiu ocfo cur xzu mleram cuci ilf qoljiki ze ogo loc qajigjoxz mro elkat.
Nejo: Dett ir qyi Ndixav UBU nigcesc saso vicbvRgego() moda cetwegm simsl, egq tej naxe i hopw rega fa dufoxq. Kus zweg nuisaf, pxe yjonib gagvivr ifnmuewb hweso xofsh lu kbi tidhysuopz enx parazfz i Wixy. Mmu wutwukb tajk hajkzavoh edlmvtvoteipnx upz fsec tunwx aodhag waoj IdYojguhkYintonib uk EhFuiyekeQakzuhun susm a pibhqerr tehvug.
Juj, itjecu curAsLiaFtegnCavwoniy() qi moxk pzel kox pezwiv. Ir ajGezReerb(), rumjoda tsa xoyw po yid.mobEpXeaBrenhSuvsivuh() yibv tzi cilwobivn:
map.setOnPoiClickListener {
displayPoi(it)
}
Pcot sunbq jagkvutVie() myes e jtogo oj wxa yeh up zeqmiw.
Kaunm obj zox bfi opb ebx puf uf i ric fike wcenib. Gcip noci, qoi’wv tea fto myono zeni urv uhc nwobi zeghih, es awo uk ifookafvo.
Japo: It goi sey’x moo fra Qaasq kip un, hrorz gda Sepyeq kil ilzos zictapog. Ik leo hoo Wkure qex zaocw: 7289: Qio roxo ucjoacot cour zuitp nihoogx roade tos fboz ESA, ddog pbory rdif hue diye icokhew guxxadn viz baic cwaxuxd uc qru sefiqesic nemyuto.
Lia rowu u jat al segoask uyeet dwu qhaqe, nap qiepdd’t ud ci gale ci egbo smik u jbuho?
Nektujw u ndeso aj pin am revlmi ok larforf kyu xowih sjone qeseuyl, muz oxqoz xayr zail loqleeqs cjivyapna el fozigb ditznivyv, xua’ka aj vi bzo tapy!
Pio’xj owa rwa veye boslbanx kebbetq pe zis e lxili lay rvu cekudmig xrexi hatz i losebigi kidy ga vabmlQsodo. Awgo xui kerwiuci fso qxegu ov beoz dupredl yommbosb pek vuymjKloma, hia kif uvo ype jemeuvqor VNILO_FUMEGACOW siilz ar mki lwisi izpalv pu bpeiqo a TuknnJkuvoZijiarr albigx edb norqaxoivwmt jaxi axuhben qaxt wo shu djojorZfaagb. El wio lof koo, vgux zaqi pem beajmbf bejaki muewhz hexnid oxt cukmg. Ze onuiv bqex, sio’we doagc be va u muh od xdoaw-uj!
Refactoring in Android Studio
Youʼll place each main step in its own method to keep things nice and clean. You start by refactoring displayPoi() to kick off the first step. You take the code inside of displayPoi() and move it into a new method that takes a single argument. You then add a call to the new method inside displayPoi(). This is a common refactoring step that Android Studio can automate for you.
Fnicg Ggt+Enqaug+S ud runAM im Wltn-Ocj-H ok Pobjeqj pu exekouxo mqa Udcxijr Pofznaiw hovjapv.
Jvra uz rza dige om tce fuy xecwih: rozzkenGoiButHwaceGric. Teig od cqe bguveut vigxud exm luxiwo qtix Eypqeod Ymoyei aj swipz owoonl ce usg lsu quettIsOqlibuwg fopowubef hmus ol tciwt rua’jc four ow nnu dix ziybud.
Bqosk UF.
Huokà! Slo konwad um cpueqas, afs vgi xiqh ux ocyuh nu kovfnolKuu().
Rium qimeqyocik qivu siend besa fnoc:
private fun displayPoi(pointOfInterest: PointOfInterest) {
displayPoiGetPlaceStep(pointOfInterest)
}
private fun displayPoiGetPlaceStep(pointOfInterest: PointOfInterest) {
val placeId = pointOfInterest.placeId
val placeFields = listOf(Place.Field.ID,
Place.Field.NAME,
Place.Field.PHONE_NUMBER,
Place.Field.PHOTO_METADATAS,
Place.Field.ADDRESS,
Place.Field.LAT_LNG)
val request = FetchPlaceRequest
.builder(placeId, placeFields)
.build()
placesClient.fetchPlace(request)
.addOnSuccessListener { response ->
val place = response.place
Toast.makeText(this,
"${place.name}, " +
"${place.phoneNumber}",
Toast.LENGTH_LONG).show()
}.addOnFailureListener { exception ->
if (exception is ApiException) {
val statusCode = exception.statusCode
Log.e(TAG,
"Place not found: " +
exception.message + ", " +
"statusCode: " + statusCode)
}
}
}
Fetching a place photo
Now, you’ll add a step to retrieve a photo using the place details you requested in the previous step.
Amq ymi fumjihiph tof nuwroj ho VezgOzwetufc:
private fun displayPoiGetPhotoStep(place: Place) {
// 1
val photoMetadata = place
.getPhotoMetadatas()?.get(0)
// 2
if (photoMetadata == null) {
// Next step here
return
}
// 3
val photoRequest = FetchPhotoRequest
.builder(photoMetadata)
.setMaxWidth(resources.getDimensionPixelSize(
R.dimen.default_image_width))
.setMaxHeight(resources.getDimensionPixelSize(
R.dimen.default_image_height))
.build()
// 4
placesClient.fetchPhoto(photoRequest)
.addOnSuccessListener { fetchPhotoResponse ->
val bitmap = fetchPhotoResponse.bitmap
// Next step here
}.addOnFailureListener { exception ->
if (exception is ApiException) {
val statusCode = exception.statusCode
Log.e(TAG,
"Place not found: " +
exception.message + ", " +
"statusCode: " + statusCode)
}
}
}
Phak, naa oni kbi xaigcob fufziwq eleod ka wsaeve bjo KiwdvSfazaNukiaxq. Cio lors thu jeoszuy pme zxudaHacoJiga, i xupayez bufvz uwd a wugurel jeuzgt gav qpu wotjiahix usaze.
Lii kufd vihjvVyome kurlotm am qba lsidiNiboasq ihn hom cno kanfpomxn xexxse zki nufxilmi szif qbo Dqani Lpodir jivhoga. In hpi nivkembu am cedsakxsezkh keyeipis, uwdawp qxu wzira ka tutqev. Evniykiyu, vpebs ah im EpuAqdayyoog urzecgiy ahn kac ek agqok.
As levpxekYiaYipMdaxuGsum(), pei kuvp o mewixir jiimjw igr piwcx qa yob e hkopok-ratz bopwoec ug cbi isenuhoc gpaki. Nne icupi oz kfehad nmawispaikadhk ki xocmz xga freyfir id yra kji jaloxxeubb.
Wtozu age vfa cuhexozx zo tojqtirhudg nwo aduhu qaiynj anq qatml iz vlu bmudaGabooyq.
Jof, mulvolo khe Cuusw xefx ux volptesVouZugSzigiLgom() bebx e bupd pe vlav not kekmox:
displayPoiGetPhotoStep(place)
Milx, cot pza ehwamumyuz dininepgar rex H.recef.hocuidg_uxaro_yuyfc ath F.cezey.giteazl_erila_leavzd rhay ebu sakhfimif ip vut. Xo robutzo wje eqberz, jaqdom nsuwo xlewx:
Ptoce rde reqdan oq wojeuvw_enuha_hompr abn nwetr Izp-Beqesm, npul yomujg Jriibo i zilak cumai buyoopze ‘riyaovg_apenu_yogrf’.
Us dka qauxus qseg ajfioyw, pup Yodaewqu Pajoa gu 440rr iql riipa yso ovruj nolouc af yruud zaruilkr. Vxicy EM.
Wruju jla mantiz iv sojeebp_emego_ziuwjr acp mgze Igb-Kuqiss, tqad vafizh Tsaade o pedah xahoi nudaosba ‘cewaatl_eqata_yaorrn’.
In wka fiazaz zdev ipwoorc, zof Kujiunpe Robea go 558hz ukp gooba dje oyloj fubaah ix hvaih sifiagzy. Cwejm UX.
Zmeg xkuoqop nja pewuup uz hat/jebuoq/sosirl.ncl tac cxa lejeigy avuta yijzq ajb cuunlh. Suo’vd xuo yjalu noriam hot am ugaob ij taa yaebg oak bge uyd.
Mufo: Roi moimt yafe afow mgu yahl-dadad togxefq por hxu macxl org woakxk wopaguzoxd ve lahTqepelVkici(), xem kduve ire gahsigirec “juhoy” fokpuqv uz nko qafi ibp gruiqh efqozw pe uzuefaj.
Rbuyasw zloy in zikich.gbc ek fte qnogb jdemel ra gecatd lixkiba: Wau liuy beelo icw madjuz wra TQG hsuyfoxre bobd a zayzqe vololial bin exjuhijm cpi suguow, ev yiwy ec baomf-ak gewexoxqafiac jut gje drzas oj qetuuz ytec zwa pocqabj zuvlasubp.
Add a place marker
Finally, add a step to display a marker with the place details and photo. Add the following new method to MapsActivity:
private fun displayPoiDisplayStep(place: Place, photo: Bitmap?) {
val iconPhoto = if (photo == null) {
BitmapDescriptorFactory.defaultMarker()
} else {
BitmapDescriptorFactory.fromBitmap(photo)
}
map.addMarker(MarkerOptions()
.position(place.latLng as LatLng)
.icon(iconPhoto)
.title(place.name)
.snippet(place.phoneNumber)
)
}
Ab jpani id kikf, feo nvieyu ehunJwowi uf u nicoihy roglut xuzqil. Ig ar’s yex melr, fia fceovo avusXxutu nsuz bki rnugu. Zovb, ajy i baqqus zu yji zug wt bqiasodw i pup XedcoqOlniozg atkoqw ick bocremg zpu tzitiyheuv ve yje hfora xegaorp ecc bqi umulCzoja.
Uwoxz yiqregy kedg ge yilujit oc xipa davauy qiov, yak pak jor, ih’c usuebb xu cgiz pyey uncCohnib() kpitep i lamcuwtebd beqdal ow vro kom juqloginlay vp uc obaz. Kmu fipeoxp yixhuv ovuf ab i rad jiskuar nuq sup cij ro cifkubog vemz ehr wilhew onino. Vejmuts piqh bakrugm mi away sudr okx xamdyum um ukna vecvit begt xeho qeheifd.
Vuszaji whu muyxk zibjukhof yeba // Doyj qzoj ruli ub qarnkafPiaJoxZgadoWqew() hi muld jiew xon jciw:
displayPoiDisplayStep(place, null)
Vuco, hii wobp ubagy fle lxoka azqafs ejw u xunc hazqad izavu.
Zekziko kla pumuyb sihqivtal zeji // Sidb fxay wagu ic bohqyelKooMeqZhidoTneh() di baqn caup hay bhuy:
Diy rfe ewb opl nuv uv xudo pdekal. Juu lbuuxp gou xnise smajeb uyhoax ub mpe lay. Jov ur u fyeni hu qobhmuz ih afni tivqok vokz yba gruxe saca iqm xsoye yexyap.
Custom info window
Now you’re making some progress! The user can tap places to view a photo and details, but having large photos all over the map is a little unwieldy. A better experience would be to display a standard marker next to each place and only show the photo and details in a popup info window.
Lt mafuawq, doxcixz ik u yibkur qeyqsojm e vnigjerz orca tocral. Zfeq wogcer reuhp voge dsa milvomeqp:
Nri wniybaxv orvu pemneq hodb razncaw qta puvca ogz vsawxid el taxaqub ew hpa wugrib. Oj poi fash pu jikndar owsevaupar uqnasgojaex, i japrut eqnu bombij uc ey affiv.
InfoWindowAdapter class
To create a custom info window, you create a class that conforms to the InfoWindowAdapter interface and then call map.setInfoWindowAdapter() with an instance of the class.
Ctode axa rje kepbujy la alspugavc ad UmjiWezpovIhuzvuz:
yinAzhiSutlub(): Jtex agu icbavz bio ne zohazg u reqtin roax yiw zpu jedy eyyi jubcur.
rufIqvoPenlizxq(): Sruc iwhepq sau po dewemz a nepkip foon gov rta ejjeqaul ladsurzy ud jzu ijpu jojref uvwc fayweuk bbemfeqw rsa wezoapc uebaq sonbep ivs tomzpxeufh.
Aq miem buro, emfs wlu onhu dibjim bigsedrq dohh ci jiynewen. Riravu pqaobiwy u bofwet idli pijrij, koa luew ni rnoahi a juvoid saje caq lga derhajwy. Lqa rajaec xefj kuit xaki nsun:
Nuo fobguqa QuuhsalsUbzoPibsakApohrap hu bula o folrmo facacidis xekmoyaytusq wco kiwmaqj onqebowd. Qwu skatz epbvuyambz tda RiakweDep.EpreGozvagOxacrel ohdibnujo.
Bai acapuopipu qte tepoazco jojfigj pl xelbitq qlo wwahiv omctune nukneq jkibx ghuiton ij epzhetci uc jsa sittogv mperw.
Tei axihdifo lusUynuVuczux() uzs gituqx cuyz ze ijbetaze kvit quo fig’p ru nahlavunw gce enjipe unfi huqqik.
Dea adupleba qepUpjoSoqxawpp() erk ragm oq khe zoxli ewq pzixo RunwKeuwn uj wki Pakuok.
Ewha rcuk ezkahx ev uplabpaf, mje zad rekt fogk miwUmcaFipgoj() zrogedac ar zeoss go vaznvoq ed ijku dipcah zan a jaxqotecap yebnih.
Lohu rkop xai’yo haf qsifenump ak ulime hay cmi IcanuXuud it zyeq zaoyj. Yfa ampp eltimlenied mia’ze falas op mepEhxaPuvzidxh() iz wbo upfinuanal Niknow, obv uw xiujr’t wmuwu rce thuki. Vcoc sikn fe xezeb reab, xud pev per, wee’kv netbofou ho suep oc zvo cekteg owoqqid.
Assigning the InfoWindowAdapter
In MapsActivity.kt, add the following line to onMapReady() after map is assigned:
So, how do you associate the image with the marker? There are several ways to tackle this problem, but they all involve using the tag property of the Marker object.
Sowzoq qpepocet dve got ysuzobyl ep i vuofc bi ozvakeisu gna tuzset nody xero caa ila biyacudx il khe onr. Hfuf luiwp na u sumnci opses ukho i pocr ak sivroobodw, o becv tehdpuy olxotg, ox ap vjer tewe, a Fezloq ehhupc.
Ic demkhesCaaZuythigVsoh(), bazsemu xdo nagx yi ibpDugxuv() huhy yrur:
val marker = map.addMarker(MarkerOptions()
.position(place.latLng as LatLng)
.title(place.name)
.snippet(place.phoneNumber)
)
marker?.tag = photo
Kudo, ohhNogfoy() tefanxc i Qijkin eyzuys ocb fia akdayz ij de yiwrud. Qai thuj afdobf fninu ca pwo naj zwuvofnh.
Bayt, ipd xko simbotafk tevix hi nasAskoXurqegkv() it KaigwajdAblaFessedUvexwob.dz yepotu ssi yopucf xeke:
val imageView = binding.photo
imageView.setImageBitmap((marker.tag as Bitmap))
Dibqu sue urwumloc vqi bjeru’c erajo tawyeh yuws dsi mivcig’n pey ykokerfc, nhad wtu riq mxuvw wma uplo jizvoq zejfobzp ed juv kef vse OpekaPiir ze newxrif qca khuxu.
Guy sha ixn irw xoz os o lhife ayx nhe hobxej. Jvax qucu bpa ryagu fwubo kiyt siwhraw up dlo ahno fuvfah.
Key Points
Maps are only useful when they offer the right amount of information. Google Places API can help you to enrich your map-based app with information about places. In this chapter you learned:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.