Show Current Location inside Google Map Fragment
Asked Answered
C

2

5

I am currently working on a Fragment with a Google Map in it. Once a user visits that fragment his map should zoom and focus on his location. However, it shows map of the world and not nearby location (near user):

@SuppressWarnings("unused")
public class DeferredMapFragment extends MapFragment implements GoogleMap.OnCameraChangeListener, OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
            LocationListener {
        private static final String TAG = "DeferredMapFragment";

    private Deque<Runnable> pendingActions;
    private AbstractMap<Marker, Object> tags;

    private GoogleMap map;
    private LocationRequest mLocationRequest;
    private GoogleApiClient mGoogleApiClient;
    private Location mLastLocation;
    private Marker marker;

    private GoogleMap.OnCameraChangeListener cameraChangeListener = null;

    private boolean isMapReady = false;

    /*
     * INTERNALS
     */

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (getMap() != null) {
            map = getMap();
            getMap().setOnCameraChangeListener(this);
        }
    }

    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
        isMapReady = true;

        if (pendingActions != null) {
            int i = pendingActions.size();
            while (i > 0) {
                pendingActions.pop().run();
                --i;
            }
        }

        if (cameraChangeListener != null) {
            cameraChangeListener.onCameraChange(cameraPosition);
        }

        if (getMap() != null) {
            getMap().setOnCameraChangeListener(cameraChangeListener);
        }
    }

    private void execute(Runnable action) {
        if (action == null) {
            return;
        }

        if (isMapReady) {
            action.run();
        } else {
            if (pendingActions == null) {
                pendingActions = new LinkedList<>();
            }
            pendingActions.add(action);
        }
    }

    /*
     * TAGGING
     */

    private void addTag(Marker key, Object value) {
        if (tags == null) {
            tags = new HashMap<>();
        }
        tags.put(key, value);
    }

    public Object getTag(Marker key) {
        return tags != null ? tags.get(key) : null;
    }

    /*
     * LISTENERS
     */

    public void setOnInfoWindowClickListener(final GoogleMap.OnInfoWindowClickListener listener) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().setOnInfoWindowClickListener(listener);
            }
        });
    }

    public void setOnCameraChangeListener(final GoogleMap.OnCameraChangeListener listener) {
        cameraChangeListener = listener;
    }

    public void setOnMarkerClickListener(final GoogleMap.OnMarkerClickListener listener) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().setOnMarkerClickListener(listener);
            }
        });
    }

    public void setOnMapClickListener(final GoogleMap.OnMapClickListener listener) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().setOnMapClickListener(listener);
            }
        });
    }

    /*
     * MAP OVERLAYS
     */

    public void addPolyline(final PolylineOptions options) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().addPolyline(options);
            }
        });
    }

    public void addPolygon(final PolygonOptions options) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().addPolygon(options);
            }
        });
    }

    public void addCircle(final CircleOptions options) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().addCircle(options);
            }
        });
    }

    public void addMarker(final MarkerOptions options) {
        addMarker(options, null);
    }

    public void addMarker(final MarkerOptions options, final Object tag) {
        execute(new Runnable() {
            @Override
            public void run() {
                Marker marker = getMap().addMarker(options);
                if (tag != null) {
                    addTag(marker, tag);
                }
            }
        });
    }

    public void addGroundOverlay(final GroundOverlayOptions options) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().addGroundOverlay(options);
            }
        });
    }

    public void addTileOverlay(final TileOverlayOptions options) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().addTileOverlay(options);
            }
        });
    }

    /*
     *  UI SETTINGS
     */
    public void setMapToolbarEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setMapToolbarEnabled(enabled);
            }
        });
    }

    public void setPadding(final int left, final int top, final int right, final int bottom) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().setPadding(left, top, right, bottom);
            }
        });
    }

    public void setZoomControlsEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setZoomControlsEnabled(enabled);
            }
        });
    }

    public void setCompassEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setCompassEnabled(enabled);
            }
        });
    }

    public void setMyLocationButtonEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setMyLocationButtonEnabled(enabled);
            }
        });
    }

    public void setIndoorLevelPickerEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setIndoorLevelPickerEnabled(enabled);
            }
        });
    }

    public void setScrollGesturesEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setScrollGesturesEnabled(enabled);
            }
        });
    }

    public void setZoomGesturesEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setZoomGesturesEnabled(enabled);
            }
        });
    }

    public void setTiltGesturesEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setTiltGesturesEnabled(enabled);
            }
        });
    }

    public void setRotateGesturesEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setRotateGesturesEnabled(enabled);
            }
        });
    }

    public void setAllGesturesEnabled(final boolean enabled) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().getUiSettings().setAllGesturesEnabled(enabled);
            }
        });
    }

    public void setInfoWindowAdapter(final GoogleMap.InfoWindowAdapter adapter) {
        execute(new Runnable() {
            @Override
            public void run() {
                getMap().setInfoWindowAdapter(adapter);
            }
        });
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        map = googleMap;

        setUpMap();

    }

    public void setUpMap() {

        map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        map.setMyLocationEnabled(true);
    }

    @Override
    public void onConnected(Bundle bundle) {

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

        //mLocationRequest.setSmallestDisplacement(0.1F);

        if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onLocationChanged(Location location) {
        mLastLocation = location;

        //remove previous current location Marker
        if (marker != null){
            marker.remove();
        }

        double dLatitude = mLastLocation.getLatitude();
        double dLongitude = mLastLocation.getLongitude();
        marker = map.addMarker(new MarkerOptions().position(new LatLng(dLatitude, dLongitude))
                .title("My Location").icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_RED)));
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(dLatitude, dLongitude), 8));

    }

    @Override
    public void onPause() {
        super.onPause();
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(getContext())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

}

Is there a zoom feature available? Also my api allows me to access zip code of user. Can I use that instead? I require user to be shown his nearby locations.

Carilla answered 19/1, 2017 at 23:40 Comment(0)
Z
13

This is similar to my other answer here, however, this is different due to the fact that you are extending the MapFragment and have your custom behavior implemented inside the Fragment.

Here is an example Fragment that extends SupportMapFragment, on launch it will get the user's current location, place a Marker, and zoom in:

public class MapFragment extends SupportMapFragment
        implements OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener {

    GoogleMap mGoogleMap;
    SupportMapFragment mapFrag;
    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;
    Location mLastLocation;
    Marker mCurrLocationMarker;

    @Override
    public void onResume() {
        super.onResume();

        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mGoogleMap == null) {
            getMapAsync(this);
        }
    }
    @Override
    public void onPause() {
        super.onPause();

        //stop location updates when Activity is no longer active
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap)
    {
        mGoogleMap=googleMap;
        mGoogleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

        //Initialize Google Play Services
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                //Location Permission already granted
                buildGoogleApiClient();
                mGoogleMap.setMyLocationEnabled(true);
            } else {
                //Request Location Permission
                checkLocationPermission();
            }
        }
        else {
            buildGoogleApiClient();
            mGoogleMap.setMyLocationEnabled(true);
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(getActivity(),
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {}

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {}

    @Override
    public void onLocationChanged(Location location)
    {
        mLastLocation = location;
        if (mCurrLocationMarker != null) {
            mCurrLocationMarker.remove();
        }

        //Place current location marker
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);
        markerOptions.title("Current Position");
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
        mCurrLocationMarker = mGoogleMap.addMarker(markerOptions);

        //move map camera
        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,11));

    }

    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(getActivity())
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(getActivity(),
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION );
                            }
                        })
                        .create()
                        .show();


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(getActivity(),
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION );
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(getActivity(),
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {

                        if (mGoogleApiClient == null) {
                            buildGoogleApiClient();
                        }
                        mGoogleMap.setMyLocationEnabled(true);
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(getActivity(), "permission denied", Toast.LENGTH_LONG).show();
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

}

Since the Location permission request needs to go through the Activity, you will need to route the result from the Activity to the Fragment's onRequestPermissionsResult() method:

public class MainActivity extends AppCompatActivity {

    MapFragment mapFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mapFragment = new MapFragment();

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.add(R.id.mapframe, mapFragment);
        transaction.commit();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        if (requestCode == MapFragment.MY_PERMISSIONS_REQUEST_LOCATION){
            mapFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
        else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

}

The layout just contains a FrameLayout where the MapFragment goes.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
    android:fitsSystemWindows="true"
    tools:context="com.danielnugent.mapapplication.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:id="@+id/mapframe"
        android:layout_marginTop="?attr/actionBarSize"
        android:layout_height="match_parent"
        android:layout_width="match_parent" />

</android.support.design.widget.CoordinatorLayout>

Result

First prompt for the Location permission:

enter image description here

Once the Location permission is granted, show the current location with a Marker:

enter image description here

If the user ever denies or revokes the Location permission, this will be shown on app launch:

First, the explanation:

enter image description here

Then, the Location permission request:

enter image description here

Zante answered 20/1, 2017 at 0:32 Comment(5)
I'm getting error can't resolve method on this line LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this).Weyermann
@piyushsingh You might have the wrong LocationListener import. Make sure you have import com.google.android.gms.location.LocationListenerZante
Is there any way to add search bar in this map fragment?Weyermann
@piyushsingh There is the PlaceAutocompleteFragment class: developers.google.com/places/android-api/autocomplete Or you can create your own custom search bar: #31137027Zante
I want to add an edittext and a button in the current fragment, how can I archive this? I'm not getting the logic to include custom layout file in the MapFragment.Weyermann
E
3

Sorry but that's just much too much overhead (above), short and quick, if you have the MapFragment, you also have to map, just do the following:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            googleMap.setMyLocationEnabled(true)
} else {
    // Show rationale and request permission.
}

zoom works like that:

val cameraPosition = CameraPosition.Builder().target(LatLng(location.latitude, location.longitude)).zoom(17.0.toFloat()).build()
            val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition)
            googleMap?.moveCamera(cameraUpdate)

Code is in Kotlin, hope you don't mind. 17.0f is some quite detailed zoom level. Just try different values.

have fun

Enrobe answered 25/3, 2018 at 6:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.