Advertisement
tuomasvaltanen

Untitled

Apr 5th, 2023 (edited)
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.92 KB | None | 0 0
  1. // Edistynyt mobiiliohjelmointi, 5.4.2023
  2.  
  3. // ------------------------------------------
  4. // Harjoitus 2 - Google Maps
  5. // ------------------------------------------
  6.  
  7. Luo ensin Google Maps API key, ks. ohje Moodlessa. Google Maps ei toimi Androidissa ilman voimassa olevaa API keytä.
  8.  
  9. Luo navigaatioeditorin kautta uusi fragment (Google Maps Fragment), ja laita se päävalikkoon (res -> menu -> activity_main_drawer.xml sekä MainActivity.kt)
  10.  
  11. Gradle Scripts -> local.properties-tiedosto:
  12.  
  13. Lisää tänne uusi muuttuja nimeltä MAPS_API_KEY, johon asetetaan oma Google MAPS API key. ALLA OLEVA ESIMERKKI EI OLE OIKEA API KEY:
  14.  
  15. MAPS_API_KEY=AIzfdsÖFKLHDLFJKaFJHDJHKFGDSHsFJKHDSKLJGhfjldaghlrieah
  16. OPENWEATHER_API_KEY=tähänoikeaopenweatherapikey
  17.  
  18. MapsFragment.kt, lisätään Rovaniemi karttaan ja säädetään zoomausta:
  19.  
  20. class MapsFragment : Fragment() {
  21.  
  22. private val callback = OnMapReadyCallback { googleMap ->
  23. // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
  24.  
  25. val sydney = LatLng(-34.0, 151.0)
  26. googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
  27.  
  28. // lisätään myös Rovaniemi kartalle
  29. val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
  30. googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
  31.  
  32. // siirretään Mapsin kamera tähän koordinaattiin
  33. // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
  34. googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
  35. }
  36.  
  37. override fun onCreateView(
  38. inflater: LayoutInflater,
  39. container: ViewGroup?,
  40. savedInstanceState: Bundle?
  41. ): View? {
  42. return inflater.inflate(R.layout.fragment_maps, container, false)
  43. }
  44.  
  45. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  46. super.onViewCreated(view, savedInstanceState)
  47. val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
  48. mapFragment?.getMapAsync(callback)
  49. }
  50. }
  51.  
  52. // MapsFragment.kt, otetaan binding layer käyttöön
  53.  
  54. class MapsFragment : Fragment() {
  55.  
  56. private var _binding: FragmentMapsBinding? = null
  57. // This property is only valid between onCreateView and
  58. // onDestroyView.
  59. private val binding get() = _binding!!
  60.  
  61. private val callback = OnMapReadyCallback { googleMap ->
  62. // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
  63.  
  64. val sydney = LatLng(-34.0, 151.0)
  65. googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
  66.  
  67. // lisätään myös Rovaniemi kartalle
  68. val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
  69. googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
  70.  
  71. // siirretään Mapsin kamera tähän koordinaattiin
  72. // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
  73. googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
  74. }
  75.  
  76. override fun onCreateView(
  77. inflater: LayoutInflater,
  78. container: ViewGroup?,
  79. savedInstanceState: Bundle?
  80. ): View? {
  81. _binding = FragmentMapsBinding.inflate(inflater, container, false)
  82. val root: View = binding.root
  83.  
  84. // käytetään binding layeria myös karttafragmentissa
  85.  
  86. return root
  87. }
  88.  
  89. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  90. super.onViewCreated(view, savedInstanceState)
  91. val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
  92. mapFragment?.getMapAsync(callback)
  93. }
  94. }
  95.  
  96. // maps_fragment.xml on ongelmallinnen koska siinä on vain fragment, jonka sisälle ei saa mitään. Kääräistään fragment ConstraintLayoutin sisälle, jolloin saamme enemmän kontrollia fragmentin ulkoasuun:
  97.  
  98. <?xml version="1.0" encoding="utf-8"?>
  99. <androidx.constraintlayout.widget.ConstraintLayout
  100. xmlns:android="http://schemas.android.com/apk/res/android"
  101. xmlns:app="http://schemas.android.com/apk/res-auto"
  102. xmlns:tools="http://schemas.android.com/tools"
  103. tools:context=".MapsFragment"
  104. android:layout_width="match_parent"
  105. android:layout_height="match_parent">
  106.  
  107. <!-- asetetaan fragment ConstraintLayoutin sisälle, jotta
  108. voimme asettaa siihen lisää Viewejä, esim. Checkboxit ja RadioButtonit jne.
  109. HUOM: xmlns:android, xmlns:tools ja tools:context pitää siirtää fragmentista
  110. ConstraintLayoutiin -->
  111. <fragment
  112. android:id="@+id/map"
  113. android:name="com.google.android.gms.maps.SupportMapFragment"
  114. android:layout_width="match_parent"
  115. android:layout_height="match_parent"
  116. app:layout_constraintBottom_toBottomOf="parent"
  117. app:layout_constraintEnd_toEndOf="parent"
  118. app:layout_constraintStart_toStartOf="parent"
  119. app:layout_constraintTop_toTopOf="parent"
  120. tools:layout="@layout/fragment_home" />
  121.  
  122. </androidx.constraintlayout.widget.ConstraintLayout>
  123.  
  124.  
  125. // Jotta pääsemme eroon "Unknown fragments" -ongelmasta, joka estää meitä käyttämästä Design-näkymään, lisää fragment-tagiin jos puuttuu:
  126.  
  127. tools:layout="@layout/fragment_home"
  128.  
  129. // lisätään CheckBox ulkoasuun:
  130.  
  131. <CheckBox
  132. android:id="@+id/checkBox_zoomControls"
  133. android:layout_width="wrap_content"
  134. android:layout_height="wrap_content"
  135. android:layout_margin="10dp"
  136. android:text="Zoom ON/OFF"
  137. app:layout_constraintStart_toStartOf="parent"
  138. app:layout_constraintTop_toTopOf="parent" />
  139.  
  140.  
  141. // jotta CheckBox pääsee käsiksi googleMap-olioon, pitää se ottaa talteen MapsFragmentin tasolle koodissa:
  142.  
  143. // tehdään luokan ylätasolle uusi jäsenmuuttuja => gMap
  144. // tarkoitus on tallentaa googleMap-olio talteen, jotta sitä voi
  145. // käyttää muualtakin kuin callbackin sisältä
  146. private lateinit var gMap : GoogleMap
  147.  
  148. private val callback = OnMapReadyCallback { googleMap ->
  149. // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
  150.  
  151. // laitetaan googleMap-olio talteen, jotta myös onCreateView
  152. // ja ym. metodit pääsevät myös siihen käsiksi
  153. gMap = googleMap
  154.  
  155. val sydney = LatLng(-34.0, 151.0)
  156. googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
  157.  
  158. // lisätään myös Rovaniemi kartalle
  159. val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
  160. googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
  161.  
  162. // siirretään Mapsin kamera tähän koordinaattiin
  163. // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
  164. googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
  165. }
  166.  
  167.  
  168. // tämän jälkeen binding -layerin kautta voidaan asettaa zoom-kontrollit päälle tai pois:
  169.  
  170. // käytetään binding layeria myös karttafragmentissa
  171. binding.checkBoxZoomControls.setOnCheckedChangeListener { compoundButton, b ->
  172. Log.d("TESTI", "CHECKED! " + b.toString())
  173. gMap.uiSettings.isZoomControlsEnabled = b
  174. }
  175.  
  176.  
  177. // nyt zoom-kontrollien päälle tulee NavigationDrawerin pohjassa oleva email-nappi (vihreä ja pyöreä), tämän voi kytketä pois näkyviltä app_bar_main.xml :ssä (android:visibility)
  178.  
  179. <com.google.android.material.floatingactionbutton.FloatingActionButton
  180. android:id="@+id/fab"
  181. android:visibility="gone"
  182. android:layout_width="wrap_content"
  183. android:layout_height="wrap_content"
  184. android:layout_gravity="bottom|end"
  185. android:layout_marginEnd="@dimen/fab_margin"
  186. android:layout_marginBottom="16dp"
  187. app:srcCompat="@android:drawable/ic_dialog_email" />
  188.  
  189.  
  190.  
  191. // lisätään myös radiogroup ja pari radiobuttonia ulkoasuun (fragment_maps):
  192.  
  193. <RadioGroup
  194. android:layout_width="wrap_content"
  195. android:layout_height="wrap_content"
  196. android:layout_margin="10dp"
  197. app:layout_constraintEnd_toEndOf="parent"
  198. app:layout_constraintTop_toTopOf="parent">
  199.  
  200. <RadioButton
  201. android:id="@+id/radioButton_map_normal"
  202. android:checked="true"
  203. android:layout_width="match_parent"
  204. android:layout_height="wrap_content"
  205. android:text="Normal" />
  206.  
  207. <RadioButton
  208. android:id="@+id/radioButton_map_hybrid"
  209. android:layout_width="match_parent"
  210. android:layout_height="wrap_content"
  211. android:text="Hybrid" />
  212. </RadioGroup>
  213.  
  214.  
  215. // jokaiselle RadioButtonille tehdään oma anonyymi funktio, joka huolehtii klikkauksesta:
  216.  
  217. // kun valitaan radiobuttonilla normaali-kartta:
  218. binding.radioButtonMapNormal.setOnCheckedChangeListener { compoundButton, b ->
  219. // OnChecked käynnistyy molempiin suuntiin, eli jos RadioButton valitaan
  220. // TAI valintaa poistuu => tulee paljon bugeja, ja RadioButtonit kilpailevat
  221. // keskenään kumpaa koodia pitäisi totella
  222. if(compoundButton.isChecked) {
  223. gMap.mapType = GoogleMap.MAP_TYPE_NORMAL
  224. }
  225. }
  226.  
  227.  
  228. samaa voi soveltaa myös Hybrdiin ym.
  229.  
  230. // SEURAAVA VAIHE, reagoidaan kun Markeria klikataan:
  231.  
  232. Vaihe 1:
  233. // nyt MapsFragment toteuttaa olio-ohjelmoinnin interfacen
  234. // nimeltä GoogleMap.OnMarkerClickListener =>
  235. // MapsFragmentista pitää nyt löytyä interfacen vaatima metodi, eli OnMarkerClick()
  236. class MapsFragment : Fragment(), GoogleMap.OnMarkerClickListener {
  237.  
  238. Vaihe 2:
  239.  
  240. // tämä metodi vastaa siitä, kun jotakin markeria klikataan kartalla
  241. // tämä vaaditaan MapsFragmentista silloin kun se toteuttaa OnMarkerClickListenerin
  242. override fun onMarkerClick(p0: Marker): Boolean {
  243. Log.d("TESTI", "Marker CLICK!")
  244. return false
  245. }
  246.  
  247. Vaihe 3: OnMapReady -callbackin viimeiseksi riviksi:
  248.  
  249. // asetetaan että tämä luokka (this => MapsFragment) huolehtii siitä
  250. // jos jotain Markeria klikataan
  251. googleMap.setOnMarkerClickListener(this)
  252.  
  253.  
  254. // jos halutaan markerista latitude ja longitude (koordinaatit), kokeile on MarkerClickissä:
  255.  
  256. Log.d("TESTI", "Marker CLICK!")
  257. Log.d("TESTI", p0.position.latitude.toString())
  258. Log.d("TESTI", p0.position.longitude.toString())
  259.  
  260.  
  261. // lisävinkki: tagit Vieweissä.
  262. // tagit ovat jokaisessa Viewissä muuttuja, mihin voi tallentaa mitä vain tietoa, esim tekstiä:
  263. // ks. esim. OnMapReadyCallback, muutetaan markerit siten, että niissä on tagissa kaupungin nimi
  264.  
  265. // Tehdään Sydneyn -marker
  266. val sydney = LatLng(-34.0, 151.0)
  267. var m1 = googleMap.addMarker(MarkerOptions().position(sydney).title("Sydney"))
  268. m1?.tag = "Sydney"
  269.  
  270. // lisätään myös Rovaniemi kartalle
  271. // jos marker tallennetaan muuttujaan, siihen voidaan asettaa lisädataa tagin kautta
  272. // esim. kaupungin nimi jos rajapinta ei jostain syystä tukisi koordinaatteja
  273. val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
  274. var m2 = googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi"))
  275. m2?.tag = "Rovaniemi"
  276.  
  277.  
  278. // nyt tagin kanssa onMarkerClick:
  279.  
  280. // tämä metodi vastaa siitä, kun jotakin markeria klikataan kartalla
  281. override fun onMarkerClick(p0: Marker): Boolean {
  282. Log.d("TESTI", "Marker CLICK!")
  283. Log.d("TESTI", p0.position.latitude.toString())
  284. Log.d("TESTI", p0.position.longitude.toString())
  285.  
  286. // jos markerilla on tagi, tällä tavalla sen saa haettua siitä
  287. Log.d("TESTI", p0.tag.toString())
  288. return false
  289. }
  290.  
  291. // OpenWeatherMap, muista tehdä tunnukset jotta saat oman API keyn: tämän jälkeen osoite on tätä muotoa:
  292.  
  293. https://api.openweathermap.org/data/2.5/weather?lat=66.50247438013193&lon=25.7300978471244&appid=OMAKEYTAHAN
  294.  
  295. // data tulee nyt Kelvineinä, vaihdetaan units metriseen järjestelmään lisäämällä units=metric osoitteen sekaan:
  296. // huom, muuttujien välissä pitää olla & -merkki jos on useampi muuttuja osoitte
  297.  
  298. https://api.openweathermap.org/data/2.5/weather?lat=66.50247438013193&lon=25.7300978471244&units=metric&appid=OMAKEYTAHAN
  299.  
  300. // esimerkkidataa:
  301.  
  302. {"coord":{"lon":25.7301,"lat":66.5025},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":3.71,"feels_like":0.89,"temp_min":3.71,"temp_max":3.71,"pressure":1029,"humidity":60},"visibility":10000,"wind":{"speed":3.09,"deg":220},"clouds":{"all":0},"dt":1680711554,"sys":{"type":1,"id":1354,"country":"FI","sunrise":1680664546,"sunset":1680715420},"timezone":10800,"id":638936,"name":"Rovaniemi","cod":200}
  303.  
  304. // tämän jälkeen json2kt.com -> Luo datan pohjalta CityWeather -luokka (+ mukana tulevat apuluokat). Aseta paketti sitten, että se vastaa projektiasi sen jälkeen, kun teet ensin datatypes ja cityweather-kansiot.
  305.  
  306. // Android-projektissa: luo uusi kansio koodikansioosi nimeltä "datatypes".
  307.  
  308. // Eli jos java-kansion sisällä ohjelmasi paketti on com.example.testi, klikkaa sitä oikealla ja luo uusi Package, nimeksi datatypes.
  309.  
  310. // koska Android Studio yhdistelee paketteja omin päin joskus, luo datatypesin sisälle Kotlin-class nimeltä Dummy. Tämä poistetaan myöhemmin.
  311.  
  312. // kun Dummy on tehty, luo kaksi pakettia lisää datatypesin sisälle -> comment ja cityweather. kopioi aiemmin tehty Comment.kt comment-kansioon.
  313.  
  314. // json2kt.com -> aseta paketiksi nyt projektisi paketti, joka viittaa cityweather -kansioon. eli jos projektisi paketti on com.example.testi, aseta silloin paketiksi com.example.testi.datatypes.cityweather.
  315.  
  316. // => luokan nimeksi CityWeather.
  317.  
  318. // lataa zip-tiedosto, pura se johonkin kansioon väliaikaseisti tietokoneella, ja kopioi sen jälkeen puretut tiedostot Android-projektissa datatypes -> cityweather -kansioon
  319.  
  320. // poista Dummy-luokka lopuksi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement