Android-native

Map di Android dengan menggunakan mapbox (Part 2)

Mapbox adalah API, SDK, dan live update map untuk membantu developer dalam membangun atau membuat pemetaan, navigasi, dan pengalaman pencarian across platforms...

Ikhwan Written by Ikhwan · 4 min read >

Mapbox adalah API, SDK, dan live update map untuk membantu developer dalam membangun atau membuat pemetaan, navigasi, dan pengalaman pencarian across platforms menjadi lebih baik dan mudah. Mapbox menurut mimin menjadi salah satu pilihan yang cukup bagus sebagai pengganti dari Google Map.

Di part sebelumnya, kita telah membahas alasan penggunakan mapbox, pembuatan akun, dan menampilkan peta. Di part ke 2 ini, kita akan membahas tentang permission dan menampilkan lokasi device atau posisi pengguna saat ini.

Yuk Ngoding

Nah kita langsung praktek ya.

1. Layout

Kita akan menggunakan layout yang sama seperti pada part sebelumnya.

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:mapbox_cameraTargetLat="-6.1753924"
        app:mapbox_cameraTargetLng="106.8271528"
        app:mapbox_cameraZoom="12" />

</androidx.constraintlayout.widget.ConstraintLayout>

2. Menampilkan peta

Pada activity kita akan menampilkan peta terlebih dahulu. Code yang digunakan sama saja dengan pada Part pertama, cuma beda activity.

package id.yukngoding.explore_mapbox.type.deviceLoc

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.mapboxsdk.Mapbox
import com.mapbox.mapboxsdk.maps.MapboxMap
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback
import com.mapbox.mapboxsdk.maps.Style
import id.yukngoding.explore_mapbox.R
import kotlinx.android.synthetic.main.activity_device_location.*

class DeviceLocationActivity : AppCompatActivity(), OnMapReadyCallback {
    lateinit var mapboxMap: MapboxMap

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_display)

        Mapbox.getInstance(this, getString(R.string.access_token))
        mapView.onCreate(savedInstanceState)
        mapView.getMapAsync(this)
    }

    override fun onMapReady(mapboxMap: MapboxMap) {
        Log.e("Ikhwan", "Trace onMapReady")
        this.mapboxMap = mapboxMap
        this.mapboxMap.setStyle(Style.TRAFFIC_DAY, Style.OnStyleLoaded {
            Log.e("Ikhwan", "Trace onMapReady, Style.OnStyleLoaded")
        })
    }

    /**
     * LIFECYCLE
     * */
    override fun onStart() {
        super.onStart()
        mapView.onStart()
    }

    override fun onResume() {
        super.onResume()
        mapView.onResume()
    }

    override fun onPause() {
        super.onPause()
        mapView.onPause()
    }

    override fun onStop() {
        super.onStop()
        mapView.onStop()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        mapView.onSaveInstanceState(outState)
    }

    override fun onDestroy() {
        super.onDestroy()
        mapView.onDestroy()
    }

    override fun onLowMemory() {
        super.onLowMemory()
        mapView.onLowMemory()
    }

}

3. Izin lokasi

Jangan lupa tambahkan izin lokasi di AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="id.yukngoding.explore_mapbox">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application ... >

         ...

    </application>

</manifest>

4. Strings.xml

Tambahkan string berikut:

<resources>
    <string name="app_name">Explore_MapBox</string>
    <string name="access_token" translatable="false">YOUR_KEY</string>
    <string name="user_location_permission_explanation">Akses lokasi dibutuhkan agar dapat menajalankan fitur peta dengan baik</string>
    <string name="user_location_permission_not_granted">Akses lokasi dibututhkan</string>

    <string name="new_location">Lokasi baru, Lat: %1$s , Lng: %2$s</string>
</resources>

5. PermissionsListener

MapBox telah menyediakan interfacenya sendiri untuk mehandle permintaan akses lokasi.

package id.yukngoding.explore_mapbox.type.deviceLoc

...

class DeviceLocationActivity : AppCompatActivity(), OnMapReadyCallback, PermissionsListener {
    
    ...

    // Variables needed to handle location permissions
    private lateinit var permissionsManager: PermissionsManager

    ...

    override fun onMapReady(mapboxMap: MapboxMap) {
        this.mapboxMap = mapboxMap
        this.mapboxMap.setStyle(Style.TRAFFIC_DAY, Style.OnStyleLoaded {
            enableLocationComponent(it)
        })
    }

    @SuppressLint("MissingPermission")
    private fun enableLocationComponent(loadedMapStyle: Style) {
        // Check if permissions are enabled and if not request
        if (PermissionsManager.areLocationPermissionsGranted(this)) {
            //TODO
        } else {
            permissionsManager = PermissionsManager(this)
            permissionsManager.requestLocationPermissions(this)
        }
    }

    /**
     * Permission
     * */
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    override fun onExplanationNeeded(permissionsToExplain: MutableList<String>?) {
        Toast.makeText(
            this, R.string.user_location_permission_explanation,
            Toast.LENGTH_LONG
        ).show();
    }

    override fun onPermissionResult(granted: Boolean) {
        if (granted) {
            mapboxMap.getStyle {
                enableLocationComponent(it)
            }
        } else {
            Toast.makeText(this, R.string.user_location_permission_not_granted, Toast.LENGTH_LONG)
                .show()
            finish()
        }
    }

}
  • Method enableLocationComponent digunakan untuk mengecek apakah pengguna telah memberikan akses lokasi atau belum. Jika belum maka lakukan request izin lokasi
  • Jangan lupa untuk menambahkan code permissionsManager pada onRequestPermissionsResult
  • onPermissionResult akan dijalankan setelah user memberikan atau menolak izin lokasi.
  • onExplanationNeeded akan dijalankan ketika user sebelumnya pernah menolak memberikan izin lokasi, lalu apps meminta izin lokasi kembali.

6. Location

Disini kita akan membuat class LocationChangeCallback untuk menghandle perubahan lokasi pada device.

package id.yukngoding.explore_mapbox.type

import android.widget.Toast
import com.mapbox.android.core.location.LocationEngineCallback
import com.mapbox.android.core.location.LocationEngineResult
import id.yukngoding.explore_mapbox.R
import id.yukngoding.explore_mapbox.type.deviceLoc.DeviceLocationActivity
import java.lang.ref.WeakReference

internal class LocationChangeCallback(activity: DeviceLocationActivity?) :

    LocationEngineCallback<LocationEngineResult> {
    private val activityWeakReference: WeakReference<DeviceLocationActivity>

    /**
     * The LocationEngineCallback interface's method which fires when the device's location has changed.
     *
     * @param result the LocationEngineResult object which has the last known location within it.
     */
    override fun onSuccess(result: LocationEngineResult) {
        val activity: DeviceLocationActivity? = activityWeakReference.get()
        if (activity != null) {
            val location = result.lastLocation ?: return

            // Create a Toast which displays the new location's coordinates
            Toast.makeText(
                activity, String.format(
                    activity.getString(R.string.new_location), result.lastLocation!!
                        .latitude.toString(), result.lastLocation!!.longitude.toString()
                ),
                Toast.LENGTH_SHORT
            ).show()

            // Pass the new location to the Maps SDK's LocationComponent
            if (activity.mapboxMap != null && result.lastLocation != null) {
                activity.mapboxMap.locationComponent.forceLocationUpdate(result.lastLocation)
            }
        }
    }

    /**
     * The LocationEngineCallback interface's method which fires when the device's location can't be captured
     *
     * @param exception the exception message
     */
    override fun onFailure(exception: Exception) {
        val activity: DeviceLocationActivity? = activityWeakReference.get()
        if (activity != null) {
            Toast.makeText(
                activity, exception.localizedMessage,
                Toast.LENGTH_SHORT
            ).show()
        }
    }

    init {
        activityWeakReference = WeakReference(activity)
    }
}

Pada class tersebut kita akan menampilkan toast di setiap perubahan lokasi device.

Kembali ke activity, tambahkan code berikut:

package id.yukngoding.explore_mapbox.type.deviceLoc

...

class DeviceLocationActivity : AppCompatActivity(), OnMapReadyCallback, PermissionsListener {
    
    ...

    // Variables needed to add the location engine
    private lateinit var locationEngine: LocationEngine
    private val DEFAULT_INTERVAL_IN_MILLISECONDS = 1000L
    private val DEFAULT_MAX_WAIT_TIME = DEFAULT_INTERVAL_IN_MILLISECONDS * 5

    private val callback: LocationChangeCallback =
        LocationChangeCallback(this)

    ...

    @SuppressLint("MissingPermission")
    private fun enableLocationComponent(loadedMapStyle: Style) {
        // Check if permissions are enabled and if not request
        if (PermissionsManager.areLocationPermissionsGranted(this)) {
            // Get an instance of the component
            val locationComponent = mapboxMap.locationComponent

            // Set the LocationComponent activation options
            val locationComponentActivationOptions =
                LocationComponentActivationOptions.builder(this, loadedMapStyle)
                    .useDefaultLocationEngine(false)
                    .build()

            // Activate with the LocationComponentActivationOptions object
            locationComponent.activateLocationComponent(locationComponentActivationOptions)

            // Enable to make component visible
            locationComponent.isLocationComponentEnabled = true

            // Move camera to device location
            locationComponent.cameraMode = CameraMode.TRACKING

            // which means an arrow will be shown outside the device location icon to
            // display the device's compass bearing.
            // RenderMode.NORMAL: Blue circle without arrow
            // RenderMode.COMPASS: Blue circle with arrow, compass bearing
            // RenderMode.GPS: White circle with blue arrow, Like navigation
            locationComponent.renderMode = RenderMode.COMPASS
            initLocationEngine()
        } else {
            permissionsManager = PermissionsManager(this)
            permissionsManager.requestLocationPermissions(this)
        }
    }

    @SuppressLint("MissingPermission")
    private fun initLocationEngine() {
        locationEngine = LocationEngineProvider.getBestLocationEngine(this)
        val request = LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
            .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
            .setMaxWaitTime(DEFAULT_MAX_WAIT_TIME).build()
        locationEngine.requestLocationUpdates(request, callback, mainLooper)
        locationEngine.getLastLocation(callback)
    }

    ...

    override fun onDestroy() {
        super.onDestroy()
        // Prevent leaks
        if (this::locationEngine.isInitialized) {
            locationEngine.removeLocationUpdates(callback)
        }
        mapView.onDestroy()
    }

}

Dengan code tersebut, map akan menampilkan posisi pengguna. Untuk code lengkap dari activity, kunjungi GithubGist berikut ini ya:

Berikut hasilnya:

Hasil
Hasil

Sekian dulu tulisan kali ini, semoga bermanfaat ya. Silahkan ditunggu untuk part selanjutnya terkait mapbox. Terima kasih.

Oh iya, di website ini ada tulisan menarik lainnya juga loh, seperti berikut:

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *