Home » Android » java – Drawing freely by fingers on Google Map

java – Drawing freely by fingers on Google Map

Posted by: admin May 14, 2020 Leave a comment

Questions:

I want to implement a custom module for free drawing on Google Map. When it comes to the implementation, I have found that the Google Map onDrag() callback and always overrides my custom onDrag() function. I am not so sure how to use my FrameLayout onDrag() to override the map click and drag motion.

Here is my working:

XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/locinfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.example.androidmapsv2.CustomMapFragment" >
    </fragment>

    <FrameLayout
        android:id="@+id/fram_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </FrameLayout>

</LinearLayout>

CustomMapFragment.java

package com.example.androidmapsv2;

import com.google.android.gms.maps.MapFragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class CustomMapFragment extends MapFragment {
     public View mOriginalContentView;
        public MapWrapperLayout mMapWrapperLayout;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {

            mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
            mMapWrapperLayout = new MapWrapperLayout(getActivity());
            mMapWrapperLayout.addView(mOriginalContentView);
            return mMapWrapperLayout;
        }

        @Override
        public View getView() {
            return mOriginalContentView;
        }

        public void setOnDragListener(MapWrapperLayout.OnDragListener onDragListener) {
            mMapWrapperLayout.setOnDragListener(onDragListener);
        }
}

MapWrapperLayout.java

public class MapWrapperLayout extends FrameLayout {
    private OnDragListener mOnDragListener;

    public MapWrapperLayout(Context context) {
        super(context);
    }

    public interface OnDragListener {
        public void onDrag(MotionEvent motionEvent);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mOnDragListener != null) {
            mOnDragListener.onDrag(ev);
        }
        return super.dispatchTouchEvent(ev);
    }

    public void setOnDragListener(OnDragListener mOnDragListener) {
        this.mOnDragListener = mOnDragListener;
    }

}

MainActivity.java

package com.example.androidmapsv2;

import java.util.ArrayList;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.Projection;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import android.app.Activity;
import android.app.FragmentManager;
import android.graphics.Color;
import android.graphics.Point;
import android.location.Location;
import android.os.Bundle;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity 
    implements OnMapClickListener {

    final int RQS_GooglePlayServices = 1;
    private GoogleMap myMap;

    Location myLocation;
    TextView tvLocInfo;
    ArrayList<LatLng> val = new ArrayList<LatLng>();  
    boolean markerClicked;
    PolygonOptions polygonOptions;
    Polygon polygon;
    FrameLayout fram_map;
    CustomMapFragment myMapFragment ;
    boolean Is_MAP_Moveable;  
    Projection projection;
    public double latitude;
    public double longitude;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvLocInfo = (TextView)findViewById(R.id.locinfo);

        FragmentManager myFragmentManager = getFragmentManager();
         myMapFragment 
            = (CustomMapFragment)myFragmentManager.findFragmentById(R.id.map);

        if (myMapFragment != null) {

            myMapFragment.getMapAsync(new OnMapReadyCallback() {
                @Override
                public void onMapReady(GoogleMap map) {
                    loadMap(map);
                }
            });


            fram_map = (FrameLayout) findViewById(R.id.fram_map);
            Is_MAP_Moveable = false; // to detect map is movable 



        } else {
            Toast.makeText(this, "Error - Map Fragment was null!!", Toast.LENGTH_SHORT).show();

        }


    }

    public void Draw_Map() {


        myMap.addPolyline(new PolylineOptions()
                .addAll(val)
                .color(Color.parseColor( "#0971b2")).width(10f));

    }


    protected void loadMap(GoogleMap map) {
        // TODO Auto-generated method stub
        myMap = map;
        myMap.setMyLocationEnabled(true);

        myMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

        myMap.setOnMapClickListener(this);
        //myMap.setOnMapLongClickListener(this);
        //myMap.setOnMarkerClickListener(this);

        markerClicked = false;
        fram_map.setOnTouchListener(new View.OnTouchListener() {   
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float x = event.getX();
                float y = event.getY();

                int x_co = Math.round(x);
                int y_co = Math.round(y);

                projection = myMap.getProjection();
                Point x_y_points = new Point(x_co, y_co);

                LatLng latLng = myMap.getProjection().fromScreenLocation(x_y_points);
                latitude = latLng.latitude;

                longitude = latLng.longitude;

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        // finger touches the screen
                        val.add(new LatLng(latitude, longitude));

                    case MotionEvent.ACTION_MOVE:
                        // finger moves on the screen
                        val.add(new LatLng(latitude, longitude));

                    case MotionEvent.ACTION_UP:
                       v.performClick();

                        // finger leaves the screen
                         Draw_Map();
                        break;
                }

                if (Is_MAP_Moveable == true) {
                    return true;

                } else {
                    return false;
                }
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }


    @Override
    protected void onResume() {

        super.onResume();

        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());

        if (resultCode == ConnectionResult.SUCCESS){
            Toast.makeText(getApplicationContext(), 
                    "isGooglePlayServicesAvailable SUCCESS", 
                    Toast.LENGTH_LONG).show();
        }else{
            GooglePlayServicesUtil.getErrorDialog(resultCode, this, RQS_GooglePlayServices);
        }

    }

    @Override
    public void onMapClick(LatLng point) {

        Is_MAP_Moveable = !Is_MAP_Moveable;
        Toast.makeText(getApplicationContext(), 
                Is_MAP_Moveable ? "drawing activated" : "drawing disabled", 
                Toast.LENGTH_SHORT).show();
//      tvLocInfo.setText(point.toString());
//      myMap.animateCamera(CameraUpdateFactory.newLatLng(point));
//      
//      markerClicked = false;
    }

//  @Override
//  public void onMapLongClick(LatLng point) {
//      tvLocInfo.setText("New marker [email protected]" + point.toString());
//      myMap.addMarker(new MarkerOptions().position(point).title(point.toString()));
//      
//      markerClicked = false;
//  }
//
//  @Override
//  public boolean onMarkerClick(Marker marker) {
//      
//      if(markerClicked){
//          
//          if(polygon != null){
//              polygon.remove();
//              polygon = null;
//          }
//          
//          polygonOptions.add(marker.getPosition());
//          polygonOptions.strokeColor(Color.RED);
//          polygonOptions.fillColor(Color.BLUE);
//          polygon = myMap.addPolygon(polygonOptions);
//      }else{
//          if(polygon != null){
//              polygon.remove();
//              polygon = null;
//          }
//          
//          polygonOptions = new PolygonOptions().add(marker.getPosition());
//          markerClicked = true;
//      }
//      
//      return true;
//  }

}
How to&Answers:

One way can be to create a View over your map and create an OnTouchListener for this View to intercept map touches:

activity_maps.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                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"
        android:name="com.google.android.gms.maps.SupportMapFragment" />

    <View
        android:id="@+id/draggable"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />

</RelativeLayout>

MapsActivity.java

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
    private GoogleMap mMap;
    private View draggableView;
    private List<LatLng> polylinePoints = new ArrayList<>();
    private Polyline polyline;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        draggableView = findViewById(R.id.draggable);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        draggableView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                LatLng position = mMap.getProjection().fromScreenLocation(
                        new Point((int) motionEvent.getX(), (int) motionEvent.getY()));

                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    if (polyline != null) {
                        polyline.remove();
                        polyline = null;
                    }
                    polyline = mMap.addPolyline(
                            new PolylineOptions().color(Color.RED).addAll(polylinePoints));
                } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE){
                    polylinePoints.add(position);
                    polyline.setPoints(polylinePoints);
                } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    // Close the polyline?
                }
                return true;
            }
        });
    }
}