Search code examples
androidgoogle-mapszoominggesturemarker

In google map using pinch to zoom in and zoom out with the current location as centre of the map


In my current android application, User will have the option to select their address by placing the marker on the map. The marker always stays in the center of the map. When the user is trying to zoom into the location by using gestures (pinching the map), the marker is changing its position, while using the zoom buttons everything is fine. How do I avoid moving the marker in the gesture case by moving the marker when using a single finger and only applying the gestures when using multiple fingers.


Solution

  • Create TouchableWrapper and use it in MapFragment

    public class TouchableWrapper extends FrameLayout {
    
        private GoogleMap mGoogleMap = null;
    
        public TouchableWrapper(Context context) {
            super(context);
        }
    
        public void setGoogleMap(GoogleMap googleMap) {
            mGoogleMap = googleMap;
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            mScaleDetector.onTouchEvent(event);
    
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
    
                case MotionEvent.ACTION_DOWN:
                    mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);
    
                long thisTime = System.currentTimeMillis();
                if (thisTime - mLastTouchTime < ViewConfiguration.getDoubleTapTimeout()) {
    
                    if (mGoogleMap != null) {
                        LatLng zoomCenter = mGoogleMap.getProjection().fromScreenLocation(new Point((int) event.getX(), (int) event.getY()));
                        float currentZoom = mGoogleMap.getCameraPosition().zoom;
    
                        int mapViewHeight = getHeight();
                        int mapViewWidth = getWidth();
    
                        Projection projection = mGoogleMap.getProjection();
    
                        geographicalPosition = projection.fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));
    
                        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(geographicalPosition.latitude, geographicalPosition.longitude), currentZoom + 1));
    
                    }
                    mLastTouchTime = -1;
                } else {
                    mLastTouchTime = thisTime;
                    mGoogleMap.getUiSettings().setZoomGesturesEnabled(false);
                }
                break;
    
                case MotionEvent.ACTION_POINTER_DOWN:
                     mGoogleMap.getUiSettings().setZoomGesturesEnabled(false);
                mGoogleMap.getUiSettings().setScrollGesturesEnabled(false);
                break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);
                break;
    
                case MotionEvent.ACTION_UP:
                   mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);
                break;
            }
    
            return super.dispatchTouchEvent(event);
        }
    
    
    
    ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(getContext(),
            new ScaleGestureDetector.SimpleOnScaleGestureListener() {
    
                private float scaleFactor = 1f;
    
                @Override
                public boolean onScale(ScaleGestureDetector detector) {
                    // store scale factor for detect zoom "direction" on end
                    scaleFactor = detector.getScaleFactor();
                    float currentZoom = mGoogleMap.getCameraPosition().zoom;
                    int mapViewHeight = getHeight();
                    int mapViewWidth = getWidth();
                    if (scaleFactor > 1) {
                        // zoom in detected
                        geographicalPosition = mGoogleMap.getProjection().fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));
                        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(geographicalPosition, currentZoom + 0.05f));
    
                    } else if (scaleFactor < 1) {
                        // zoom out detected
                        geographicalPosition = mGoogleMap.getProjection().fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));
                        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(geographicalPosition, currentZoom - 0.05f));
                    }
                    return true;
                }
    
                @Override
                public void onScaleEnd(ScaleGestureDetector detector) {
    
                    super.onScaleEnd(detector);
                }
            });
    
    }
    

    MapFragment for that should be customized like:

    public class MultiTouchMapFragment extends MapFragment {
        public View mOriginalContentView;
        public TouchableWrapper mTouchView;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
            mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
            mTouchView = new TouchableWrapper(getActivity());
            mTouchView.addView(mOriginalContentView);
            return mTouchView;
        }
    
        @Override
        public View getView() {
            return mOriginalContentView;
        }
    }
    

    in MainActivity:

    public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
    
        private GoogleMap mGoogleMap;
        private MultiTouchMapFragment mMapFragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mMapFragment = (MultiTouchMapFragment) getFragmentManager()
                    .findFragmentById(R.id.map_fragment);
            mMapFragment.getMapAsync(this);
        }
    
        @Override
        public void onMapReady(GoogleMap googleMap) {
            mGoogleMap = googleMap;
            mMapFragment.mTouchView.setGoogleMap(mGoogleMap);
        }
    
    }