Moving Map Under Marker Like UBER: Maps Android, Kotlin | RRTutors

Last updated Jul 03, 2019

In this tutorial we will implement a Uber maps feature like moving map under marker with animation. This feature has implemented in many Ride applications. This feature is very usefull because it is very intuitive as compared to marker drag and drop. Before going to code, you need to ensure that you have setup Google Map SDK . For this lets check this Google Maps Documentation

Now just look at our end result should be like this.

Lets create xml file like below

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
              xmlns:tools="https://schemas.android.com/tools"
              xmlns:map="https://schemas.android.com/apk/res-auto"
              android:layout_height="match_parent"
              android:layout_width="match_parent"
              android:orientation="vertical"
        >

    <fragment
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/map"
            tools:context=".ui.MapsActivity"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            />
  
    <ImageView
            android:id="@+id/img_marker"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            map:srcCompat="@drawable/ic_map_pin"
            />
</RelativeLayout>

 

Now come to our Activity code

package com.rrtutors.mycab.ui

import android.annotation.SuppressLint
import android.app.Activity
import android.content.*
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.widget.RadioButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.LocationSettingsRequest

import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.material.tabs.TabLayout
import com.rrtutors.mycab.R
import java.security.Provider
import kotlin.properties.Delegates

class MapsActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnCameraMoveStartedListener,
    GoogleMap.OnCameraMoveListener,
    GoogleMap.OnCameraMoveCanceledListener,
    GoogleMap.OnCameraIdleListener{



    private val REQUEST_CHECK_SETTINGS: Int=101;
    private lateinit var mMap: GoogleMap
    var PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION=101;
    private lateinit var fusedLocationClient: FusedLocationProviderClient;
    private lateinit var lastLocation: Location;
    private lateinit var locationRequest: LocationRequest;

   
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment
       

        mapFragment.getMapAsync(this)
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        
    }



    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap

        // Add a marker in Sydney and move the camera
        val sydney = LatLng(-34.0, 151.0)
        mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(
                    this.getApplicationContext(),
                    android.Manifest.permission.ACCESS_FINE_LOCATION
                )
                == PackageManager.PERMISSION_GRANTED
            ) {
                mMap.isMyLocationEnabled = true;
                mMap.uiSettings.isMapToolbarEnabled = true;
                mMap.uiSettings.isMyLocationButtonEnabled = true;
                checkLocationService();
            } else {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION
                );
            }
        }else
        {
            mMap.isMyLocationEnabled = true;
            mMap.uiSettings.isMapToolbarEnabled = true;
            mMap.uiSettings.isMyLocationButtonEnabled = true;
            checkLocationService();
        }

            mMap.setOnCameraMoveStartedListener (this)
            mMap.setOnCameraIdleListener (this)
            mMap.setOnCameraMoveListener  (this)

    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {
            mMap.isMyLocationEnabled=true;
            mMap.uiSettings.isMapToolbarEnabled=true;
            mMap.uiSettings.isMyLocationButtonEnabled=true;
            checkLocationService();
        }
    }

        fun fetchCurrentLocation() {
            fusedLocationClient.lastLocation.addOnSuccessListener(this) { location ->
                // Got last known location. In some rare situations this can be null.
                // 3
                if (location != null) {
                    lastLocation = location
                    val currentLatLng = LatLng(location.latitude, location.longitude)
                    mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 12f))
                }
            }
        }

    fun checkLocationService() {

            locationRequest = LocationRequest.create();
            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            locationRequest.setInterval(10 * 1000);
            locationRequest.setFastestInterval(2 * 1000);


        val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
       // builder.setAlwaysShow(true);
        val client = LocationServices.getSettingsClient(this)
        val task = client.checkLocationSettings(builder.build())
        task.addOnSuccessListener(this) {it->
            it.locationSettingsStates;
            fetchCurrentLocation();
        }

        task.addOnFailureListener(this) { e ->
            if (e is ResolvableApiException) {
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                try {
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    e.startResolutionForResult(
                        this@MapsActivity,
                        REQUEST_CHECK_SETTINGS
                    )
                } catch (sendEx: IntentSender.SendIntentException) {
                    // Ignore the error.
                }

            }
        }
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

        if (requestCode == REQUEST_CHECK_SETTINGS) {
            if (resultCode == Activity.RESULT_OK) {
                val result = data!!.getStringExtra("result")
                fetchCurrentLocation();
            }
            else if (resultCode == Activity.RESULT_CANCELED) {
                //Write your code if there's no result
            }
        }
    }




    override fun onCameraMoveStarted(p0: Int) {
       Log.v("Onmove start","Onmove "+p0);
        mMap.clear()
    }

    override fun onCameraMove() {

        Log.v("Onmove ","Onmove ");
    }

    override fun onCameraMoveCanceled() {
        Log.v("Onmove cancel","Onmove ");
    }

    override fun onCameraIdle() {
        Log.v("Onmove Idle","Onmove ");
        val markerOptions = MarkerOptions().position(mMap.cameraPosition.target)

        mMap.addMarker(markerOptions)
    }

}

 

Here the marker and pin was handling inside onCameraIdle() and  onCameraMoveStarted() functions.

 

Related Topics

Android NavigationView, NavigationDrawer Layout

GooglePLay InApp Purchase Integration

Android OnBoarding Screen UI

Android JobScheduler

 

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" xmlns:map="https://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" > <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/map" tools:context=".ui.MapsActivity" android:name="com.google.android.gms.maps.SupportMapFragment" /> <ImageView android:id="@+id/img_marker" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" map:srcCompat="@drawable/ic_map_pin" /> </RelativeLayout>

 

Now come to our Activity code

package com.rrtutors.mycab.ui import android.annotation.SuppressLint import android.app.Activity import android.content.* import android.content.pm.PackageManager import android.location.Location import android.location.LocationListener import android.location.LocationManager import android.os.Build import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.widget.RadioButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.api.GoogleApiClient import com.google.android.gms.common.api.ResolvableApiException import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationSettingsRequest import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MarkerOptions import com.google.android.material.tabs.TabLayout import com.rrtutors.mycab.R import java.security.Provider import kotlin.properties.Delegates class MapsActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener, GoogleMap.OnCameraMoveCanceledListener, GoogleMap.OnCameraIdleListener{ private val REQUEST_CHECK_SETTINGS: Int=101; private lateinit var mMap: GoogleMap var PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION=101; private lateinit var fusedLocationClient: FusedLocationProviderClient; private lateinit var lastLocation: Location; private lateinit var locationRequest: LocationRequest; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_maps) // Obtain the SupportMapFragment and get notified when the map is ready to be used. val mapFragment = supportFragmentManager .findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) } override fun onMapReady(googleMap: GoogleMap) { mMap = googleMap // Add a marker in Sydney and move the camera val sydney = LatLng(-34.0, 151.0) mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney")) mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)) if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) { if (ContextCompat.checkSelfPermission( this.getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { mMap.isMyLocationEnabled = true; mMap.uiSettings.isMapToolbarEnabled = true; mMap.uiSettings.isMyLocationButtonEnabled = true; checkLocationService(); } else { ActivityCompat.requestPermissions( this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION ); } }else { mMap.isMyLocationEnabled = true; mMap.uiSettings.isMapToolbarEnabled = true; mMap.uiSettings.isMyLocationButtonEnabled = true; checkLocationService(); } mMap.setOnCameraMoveStartedListener (this) mMap.setOnCameraIdleListener (this) mMap.setOnCameraMoveListener (this) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (ContextCompat.checkSelfPermission(this.getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mMap.isMyLocationEnabled=true; mMap.uiSettings.isMapToolbarEnabled=true; mMap.uiSettings.isMyLocationButtonEnabled=true; checkLocationService(); } } fun fetchCurrentLocation() { fusedLocationClient.lastLocation.addOnSuccessListener(this) { location -> // Got last known location. In some rare situations this can be null. // 3 if (location != null) { lastLocation = location val currentLatLng = LatLng(location.latitude, location.longitude) mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 12f)) } } } fun checkLocationService() { locationRequest = LocationRequest.create(); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); locationRequest.setInterval(10 * 1000); locationRequest.setFastestInterval(2 * 1000); val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest); // builder.setAlwaysShow(true); val client = LocationServices.getSettingsClient(this) val task = client.checkLocationSettings(builder.build()) task.addOnSuccessListener(this) {it-> it.locationSettingsStates; fetchCurrentLocation(); } task.addOnFailureListener(this) { e -> if (e is ResolvableApiException) { // Location settings are not satisfied, but this can be fixed // by showing the user a dialog. try { // Show the dialog by calling startResolutionForResult(), // and check the result in onActivityResult(). e.startResolutionForResult( this@MapsActivity, REQUEST_CHECK_SETTINGS ) } catch (sendEx: IntentSender.SendIntentException) { // Ignore the error. } } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_CHECK_SETTINGS) { if (resultCode == Activity.RESULT_OK) { val result = data!!.getStringExtra("result") fetchCurrentLocation(); } else if (resultCode == Activity.RESULT_CANCELED) { //Write your code if there's no result } } } override fun onCameraMoveStarted(p0: Int) { Log.v("Onmove start","Onmove "+p0); mMap.clear() } override fun onCameraMove() { Log.v("Onmove ","Onmove "); } override fun onCameraMoveCanceled() { Log.v("Onmove cancel","Onmove "); } override fun onCameraIdle() { Log.v("Onmove Idle","Onmove "); val markerOptions = MarkerOptions().position(mMap.cameraPosition.target) mMap.addMarker(markerOptions) } }

 

Here the marker and pin was handling inside onCameraIdle() and  onCameraMoveStarted() functions.

 

Related Topics

Android NavigationView, NavigationDrawer Layout

GooglePLay InApp Purchase Integration

Android OnBoarding Screen UI

Android JobScheduler

 

-->
Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

3473 Views