Android: Drag map while keeping marker at the center

16,123

Solution 1

Having a map under an ImageView with your marker icon would be the best way to go here (just a framelayout with your map fragment under and your marker image centered on top should work). Then you can use an OnCameraIdleListener which would receive a callback when the map movement has ended. So just set an instance of OnCameraIdleListener to your map to listen for movement callbacks like so:

map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
        @Override
        public void onCameraIdle() {
            //get latlng at the center by calling
            LatLng midLatLng = map.getCameraPosition().target;
        }
    });

Solution 2

Try at first this layout for your Activity_map.xml:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment"
    />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/gps"
    />

</RelativeLayout>
Share:
16,123
AIS
Author by

AIS

Updated on June 05, 2022

Comments

  • AIS
    AIS about 2 years

    I am currently getting Current Location of device (lat,lng) in MapActivity. Now , I want to let the user move the map, while keeping the marker at the center. This means that whenever the map is moved, current location will be updated. (Right?)

    I followed this How to move a map under a marker? and How to attach a flexible marker on map something like Uber and Lyft? but couldn't get my concept clear.

    Activity_map.xml (Here I have added ImageView)

    <RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment"
        />
    
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/gps"
        />
    
     </RelativeLayout>
    

    My MapActivity is as follows :

     public class MapActivity extends FragmentActivity implements      OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener,GoogleMap.OnInfoWindowClickListener{
    
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);
       // int x =_st.jsonArray.length();
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            checkLocationPermission();
        }
    
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    
        fragmentOnMap = (FragmentOnMap)getFragmentManager().findFragmentById(R.id.fragment_bottom);
        getFragmentManager().beginTransaction().hide(fragmentOnMap);
    
    }
    
    @Override
    public void onMapReady(GoogleMap googleMap) {
    
    
        //Get Last Known Location and convert into Current Longitute and Latitude
        final LocationManager mlocManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        final Location currentGeoLocation = mlocManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
    
        double currentLat = currentGeoLocation.getLatitude();
       double currentLon = currentGeoLocation.getLongitude();
    
        LatLng currentLatLng = new LatLng(currentLat,currentLon);
        //Toast.makeText(this,"Cur Lat" +currentLat+"Lon"+currentLon,Toast.LENGTH_LONG).show();
    
        // Get JSON String and Break it down into individual nodes
        //double x=_location.getLatitude();
        //double y=_location.getLongitude();
        json_string = getIntent().getExtras().getString("JSON_DTA");
        LatLng latLng = null;
        try {
            int count = 0;
            //jsonObject = new JSONObject(json_string);
            jsonArray = new JSONArray(json_string);
    
            while (count < jsonArray.length()) {
                JSONObject JO = jsonArray.getJSONObject(count);
                _id = JO.getInt("id");
                _doctorname = JO.getString("doctorname");
                _doclat = JO.getString("latitude");
                _doclong = JO.getString("longitude");
    
                count++;
    
                Double d = Double.parseDouble(_doclat);
                Double e = Double.parseDouble(_doclong);
                latLng = new LatLng(d, e);
    
                DecimalFormat df = new DecimalFormat("####0.0");
    
                DistanceBetweenPoints distanceBetweenPoints = new DistanceBetweenPoints();
                double  distance = distanceBetweenPoints.CalculationByDistance(currentLatLng,latLng);
    
                double distanceThreshHold = 7 ;
                if(distance < distanceThreshHold  )
                {
                    mMap = googleMap;
                    mMap.setOnInfoWindowClickListener(this);
                    MarkerOptions _markeroptions = new MarkerOptions()
                            .position(latLng)
                            .title(_doctorname+":  " + df.format(distance) + " KM" )
                            .icon(BitmapDescriptorFactory.fromResource(R.drawable.smallest_logo));
                    mMap.addMarker(_markeroptions);
    
                    mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
    
    
                }
                else
                {
                    mMap = googleMap;
                }
    
         }
    
    
        } catch (JSONException e) {
            e.printStackTrace();
        }
    
        //Initialize Google Play Services
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                buildGoogleApiClient();
    
                mMap.setMyLocationEnabled(true);
    
            }
        } else {
            buildGoogleApiClient();
            mMap.setMyLocationEnabled(true);
        }
    }
    
    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }
    @Override
    public void onConnected(@Nullable Bundle bundle) {
    
        mLocationRequest = new LocationRequest();
    
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    
    }
    
    @Override
    public void onConnectionSuspended(int i) {
    
    }
    
    
    @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.draggable(true);
    
            markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
            mCurrLocationMarker = mMap.addMarker(markerOptions);
    
    
          //  Toast.makeText(this, "Current" + latLng, Toast.LENGTH_LONG).show();
            String tag = null;
            Log.d(tag, "lon: " + location.getLongitude() + " ----- lat: " + location.getLatitude());
    
    
            //move map camera
            mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
            mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
    
    
            //stop location updates
            if (mGoogleApiClient != null) {
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            }
    
    
        }
    
    
    
    
    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    
    }
    
    public boolean checkLocationPermission(){
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
    
            // Asking user if explanation is needed
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
    
                // Show an expanation 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.
    
                //Prompt the user once explanation has been shown
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
    
    
            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
            }
            return false;
        } else {
            return true;
        }
    }
    @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.
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {
    
                        if (mGoogleApiClient == null) {
                            buildGoogleApiClient();
                        }
                        mMap.setMyLocationEnabled(true);
                    }
    
                } else {
    
                    // Permission denied, Disable the functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
                }
                return;
            }
    
            // other 'case' lines to check for other permissions this app might  request.
            //You can add here other case statements according to your requirement.
        }
    }
    

    Your help will much appreciated. Thank you.

  • AIS
    AIS over 7 years
    So my ImageView will be on top of map fragment?
  • Andrii Omelchenko
    Andrii Omelchenko over 7 years
    Yes, it should be on top of map fragment.
  • AIS
    AIS over 7 years
    Edited...see original post
  • Joey Dalu
    Joey Dalu over 7 years
    and make sure to center it (android:layout_gravity="center") if you're using a frame layout, else it appears at the top left.
  • Andrii Omelchenko
    Andrii Omelchenko over 7 years
    1) remove android:orientation="vertical" for your root RelativeLayout 2) remove xmlns:android="http://schemas.android.com/apk/res/android" and xmlns:tools="http://schemas.android.com/tools" from your map fragment; 3) change android:name="com.google.android.gms.maps.SupportMapFragment‌​" to class="com.google.android.gms.maps.SupportMapFragment" as provided here. It should help.
  • AIS
    AIS over 7 years
    Not using xmlns:android="schemas.android.com/apk/res/android"> gave an error..hence its there
  • Andrii Omelchenko
    Andrii Omelchenko over 7 years
    You should 2) remove xmlns:android="schemas.android.com/apk/res/android" and xmlns:tools="schemas.android.com/tools" from your map fragment not from root RelativeLayout from root RelativeLayout You should remove only android:orientation="vertical" text.
  • AIS
    AIS over 7 years
    Okay, now Im getting imageview on top of map...now I want this to show my current Location, just like I am showing it my MyActivity code.
  • Andrii Omelchenko
    Andrii Omelchenko over 7 years
    CameraPosition center = mGoogleMap.getCameraPosition(); where mGoogleMap got in public void onMapReady(GoogleMap googleMap) { mGoogleMap = googleMap; }
  • AIS
    AIS over 7 years
    Added CameraPosition center = mGoogleMap.getCameraPosition();
  • AIS
    AIS over 7 years
    Sorry, added LatLng center = mMap.getCameraPosition().target; Toast.makeText(this, "Current" + center, Toast.LENGTH_LONG).show(); at the end of OnMapReady method, but got 0/0
  • AIS
    AIS over 7 years
    This has to be called in OnMapReady method?
  • AIS
    AIS over 7 years
    Solved by using yours and @Joey Dalu 's answers. Can you tell me why I had to make changes in map xml layout?
  • Joey Dalu
    Joey Dalu over 7 years
    @AIS Yes, set the OnCameraIdleListener after OnMapReady has been called. Or are you asking about something else?
  • Naveed Ahmad
    Naveed Ahmad about 6 years
    getting incorrect location map.getCameraPosition().target, as my map is showing I am in pakistan and I am getting lat long of egypt
  • Muthu S
    Muthu S about 5 years
    In my case if i used setOnCameraIdleListener, this method keep on triggering when it is idle. I need only once after map dragged.. How to go further:-| Thanks
  • mani
    mani over 2 years
    setOnCameraIdleListener should be called in OnMapReady function.