Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Edistynyt mobiiliohjelmointi, 5.4.2023
- // ------------------------------------------
- // Harjoitus 2 - Google Maps
- // ------------------------------------------
- Luo ensin Google Maps API key, ks. ohje Moodlessa. Google Maps ei toimi Androidissa ilman voimassa olevaa API keytä.
- Luo navigaatioeditorin kautta uusi fragment (Google Maps Fragment), ja laita se päävalikkoon (res -> menu -> activity_main_drawer.xml sekä MainActivity.kt)
- Gradle Scripts -> local.properties-tiedosto:
- Lisää tänne uusi muuttuja nimeltä MAPS_API_KEY, johon asetetaan oma Google MAPS API key. ALLA OLEVA ESIMERKKI EI OLE OIKEA API KEY:
- MAPS_API_KEY=AIzfdsÖFKLHDLFJKaFJHDJHKFGDSHsFJKHDSKLJGhfjldaghlrieah
- OPENWEATHER_API_KEY=tähänoikeaopenweatherapikey
- MapsFragment.kt, lisätään Rovaniemi karttaan ja säädetään zoomausta:
- class MapsFragment : Fragment() {
- private val callback = OnMapReadyCallback { googleMap ->
- // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
- val sydney = LatLng(-34.0, 151.0)
- googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
- // lisätään myös Rovaniemi kartalle
- val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
- googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
- // siirretään Mapsin kamera tähän koordinaattiin
- // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
- googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
- }
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_maps, container, false)
- }
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
- mapFragment?.getMapAsync(callback)
- }
- }
- // MapsFragment.kt, otetaan binding layer käyttöön
- class MapsFragment : Fragment() {
- private var _binding: FragmentMapsBinding? = null
- // This property is only valid between onCreateView and
- // onDestroyView.
- private val binding get() = _binding!!
- private val callback = OnMapReadyCallback { googleMap ->
- // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
- val sydney = LatLng(-34.0, 151.0)
- googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
- // lisätään myös Rovaniemi kartalle
- val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
- googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
- // siirretään Mapsin kamera tähän koordinaattiin
- // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
- googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
- }
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- _binding = FragmentMapsBinding.inflate(inflater, container, false)
- val root: View = binding.root
- // käytetään binding layeria myös karttafragmentissa
- return root
- }
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
- mapFragment?.getMapAsync(callback)
- }
- }
- // 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:
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.ConstraintLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- tools:context=".MapsFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- asetetaan fragment ConstraintLayoutin sisälle, jotta
- voimme asettaa siihen lisää Viewejä, esim. Checkboxit ja RadioButtonit jne.
- HUOM: xmlns:android, xmlns:tools ja tools:context pitää siirtää fragmentista
- ConstraintLayoutiin -->
- <fragment
- android:id="@+id/map"
- android:name="com.google.android.gms.maps.SupportMapFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:layout="@layout/fragment_home" />
- </androidx.constraintlayout.widget.ConstraintLayout>
- // Jotta pääsemme eroon "Unknown fragments" -ongelmasta, joka estää meitä käyttämästä Design-näkymään, lisää fragment-tagiin jos puuttuu:
- tools:layout="@layout/fragment_home"
- // lisätään CheckBox ulkoasuun:
- <CheckBox
- android:id="@+id/checkBox_zoomControls"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:text="Zoom ON/OFF"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- // jotta CheckBox pääsee käsiksi googleMap-olioon, pitää se ottaa talteen MapsFragmentin tasolle koodissa:
- // tehdään luokan ylätasolle uusi jäsenmuuttuja => gMap
- // tarkoitus on tallentaa googleMap-olio talteen, jotta sitä voi
- // käyttää muualtakin kuin callbackin sisältä
- private lateinit var gMap : GoogleMap
- private val callback = OnMapReadyCallback { googleMap ->
- // tämä käynnistyy silloin kun kartta on valmis ja voidaan käyttää eri ominaisuuksia
- // laitetaan googleMap-olio talteen, jotta myös onCreateView
- // ja ym. metodit pääsevät myös siihen käsiksi
- gMap = googleMap
- val sydney = LatLng(-34.0, 151.0)
- googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
- // lisätään myös Rovaniemi kartalle
- val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
- googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi!"))
- // siirretään Mapsin kamera tähän koordinaattiin
- // muutetaan siten että zoomataan tietylle tasolle, zoom-tason pitää olla float-muodossa
- googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rovaniemi, 17f))
- }
- // tämän jälkeen binding -layerin kautta voidaan asettaa zoom-kontrollit päälle tai pois:
- // käytetään binding layeria myös karttafragmentissa
- binding.checkBoxZoomControls.setOnCheckedChangeListener { compoundButton, b ->
- Log.d("TESTI", "CHECKED! " + b.toString())
- gMap.uiSettings.isZoomControlsEnabled = b
- }
- // 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)
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/fab"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="@dimen/fab_margin"
- android:layout_marginBottom="16dp"
- app:srcCompat="@android:drawable/ic_dialog_email" />
- // lisätään myös radiogroup ja pari radiobuttonia ulkoasuun (fragment_maps):
- <RadioGroup
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent">
- <RadioButton
- android:id="@+id/radioButton_map_normal"
- android:checked="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Normal" />
- <RadioButton
- android:id="@+id/radioButton_map_hybrid"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Hybrid" />
- </RadioGroup>
- // jokaiselle RadioButtonille tehdään oma anonyymi funktio, joka huolehtii klikkauksesta:
- // kun valitaan radiobuttonilla normaali-kartta:
- binding.radioButtonMapNormal.setOnCheckedChangeListener { compoundButton, b ->
- // OnChecked käynnistyy molempiin suuntiin, eli jos RadioButton valitaan
- // TAI valintaa poistuu => tulee paljon bugeja, ja RadioButtonit kilpailevat
- // keskenään kumpaa koodia pitäisi totella
- if(compoundButton.isChecked) {
- gMap.mapType = GoogleMap.MAP_TYPE_NORMAL
- }
- }
- samaa voi soveltaa myös Hybrdiin ym.
- // SEURAAVA VAIHE, reagoidaan kun Markeria klikataan:
- Vaihe 1:
- // nyt MapsFragment toteuttaa olio-ohjelmoinnin interfacen
- // nimeltä GoogleMap.OnMarkerClickListener =>
- // MapsFragmentista pitää nyt löytyä interfacen vaatima metodi, eli OnMarkerClick()
- class MapsFragment : Fragment(), GoogleMap.OnMarkerClickListener {
- Vaihe 2:
- // tämä metodi vastaa siitä, kun jotakin markeria klikataan kartalla
- // tämä vaaditaan MapsFragmentista silloin kun se toteuttaa OnMarkerClickListenerin
- override fun onMarkerClick(p0: Marker): Boolean {
- Log.d("TESTI", "Marker CLICK!")
- return false
- }
- Vaihe 3: OnMapReady -callbackin viimeiseksi riviksi:
- // asetetaan että tämä luokka (this => MapsFragment) huolehtii siitä
- // jos jotain Markeria klikataan
- googleMap.setOnMarkerClickListener(this)
- // jos halutaan markerista latitude ja longitude (koordinaatit), kokeile on MarkerClickissä:
- Log.d("TESTI", "Marker CLICK!")
- Log.d("TESTI", p0.position.latitude.toString())
- Log.d("TESTI", p0.position.longitude.toString())
- // lisävinkki: tagit Vieweissä.
- // tagit ovat jokaisessa Viewissä muuttuja, mihin voi tallentaa mitä vain tietoa, esim tekstiä:
- // ks. esim. OnMapReadyCallback, muutetaan markerit siten, että niissä on tagissa kaupungin nimi
- // Tehdään Sydneyn -marker
- val sydney = LatLng(-34.0, 151.0)
- var m1 = googleMap.addMarker(MarkerOptions().position(sydney).title("Sydney"))
- m1?.tag = "Sydney"
- // lisätään myös Rovaniemi kartalle
- // jos marker tallennetaan muuttujaan, siihen voidaan asettaa lisädataa tagin kautta
- // esim. kaupungin nimi jos rajapinta ei jostain syystä tukisi koordinaatteja
- val rovaniemi = LatLng(66.50247438013193, 25.7300978471244)
- var m2 = googleMap.addMarker(MarkerOptions().position(rovaniemi).title("Rovaniemi"))
- m2?.tag = "Rovaniemi"
- // nyt tagin kanssa onMarkerClick:
- // tämä metodi vastaa siitä, kun jotakin markeria klikataan kartalla
- override fun onMarkerClick(p0: Marker): Boolean {
- Log.d("TESTI", "Marker CLICK!")
- Log.d("TESTI", p0.position.latitude.toString())
- Log.d("TESTI", p0.position.longitude.toString())
- // jos markerilla on tagi, tällä tavalla sen saa haettua siitä
- Log.d("TESTI", p0.tag.toString())
- return false
- }
- // OpenWeatherMap, muista tehdä tunnukset jotta saat oman API keyn: tämän jälkeen osoite on tätä muotoa:
- https://api.openweathermap.org/data/2.5/weather?lat=66.50247438013193&lon=25.7300978471244&appid=OMAKEYTAHAN
- // data tulee nyt Kelvineinä, vaihdetaan units metriseen järjestelmään lisäämällä units=metric osoitteen sekaan:
- // huom, muuttujien välissä pitää olla & -merkki jos on useampi muuttuja osoitte
- https://api.openweathermap.org/data/2.5/weather?lat=66.50247438013193&lon=25.7300978471244&units=metric&appid=OMAKEYTAHAN
- // esimerkkidataa:
- {"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}
- // 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.
- // Android-projektissa: luo uusi kansio koodikansioosi nimeltä "datatypes".
- // Eli jos java-kansion sisällä ohjelmasi paketti on com.example.testi, klikkaa sitä oikealla ja luo uusi Package, nimeksi datatypes.
- // koska Android Studio yhdistelee paketteja omin päin joskus, luo datatypesin sisälle Kotlin-class nimeltä Dummy. Tämä poistetaan myöhemmin.
- // kun Dummy on tehty, luo kaksi pakettia lisää datatypesin sisälle -> comment ja cityweather. kopioi aiemmin tehty Comment.kt comment-kansioon.
- // 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.
- // => luokan nimeksi CityWeather.
- // lataa zip-tiedosto, pura se johonkin kansioon väliaikaseisti tietokoneella, ja kopioi sen jälkeen puretut tiedostot Android-projektissa datatypes -> cityweather -kansioon
- // poista Dummy-luokka lopuksi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement