1

I'm trying to draw a resizable circle on google map, which the user will be able to expand or shrink using touch gestures and also want it to work like zooming in/out option in the map, only that just the circle will get bigger/smaller on the map.

I can draw a circle on the map but i don't know how to get it to respond to touch events.

Here is my code

MainActivity.java

    public class MainActivity extends FragmentActivity implements LocationListener {

    GoogleMap googleMap;

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

        // Getting Google Play availability status
        int status = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(getBaseContext());

        // Showing status
        if (status != ConnectionResult.SUCCESS) { // Google Play Services are
                                                    // not available

            int requestCode = 10;
            Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this,
                    requestCode);
            dialog.show();

        } else { // Google Play Services are available

            // Getting reference to the SupportMapFragment of activity_main.xml
            SupportMapFragment fm = (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);

            // Getting GoogleMap object from the fragment
            googleMap = fm.getMap();

            // Enabling MyLocation Layer of Google Map
            googleMap.setMyLocationEnabled(true);

            // Getting LocationManager object from System Service
            // LOCATION_SERVICE
            LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

            // Creating a criteria object to retrieve provider
            Criteria criteria = new Criteria();

            // Getting the name of the best provider
            String provider = locationManager.getBestProvider(criteria, true);

            // Getting Current Location
            Location location = locationManager.getLastKnownLocation(provider);

            if (location != null) {
                onLocationChanged(location);
            }

            locationManager.requestLocationUpdates(provider, 20000, 0, this);
        }

    }

    @Override
    public void onLocationChanged(Location location) {

        // TextView tvLocation = (TextView) findViewById(R.id.tv_location);

        // Getting latitude of the current location
        double latitude = location.getLatitude();

        // Getting longitude of the current location
        double longitude = location.getLongitude();

        // Creating a LatLng object for the current location
        LatLng latLng = new LatLng(latitude, longitude);

        // Showing the current location in Google Map
        googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));

        // Zoom in the Google Map
        googleMap.animateCamera(CameraUpdateFactory.zoomTo(15));

        // Setting latitude and longitude in the TextView tv_location
        // tvLocation.setText("Latitude:" + latitude + ", Longitude:"+ longitude
        // );

    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub
    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub
    }

    @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;
    }
}

Main.xml

<RelativeLayout 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"
    tools:context=".MainActivity" >

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

  <in.locationingooglemapv2.CircleTouchView
        android:id="@+id/circle_drawer_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:focusable="false"/>


</RelativeLayout>

CircleTouchView.java

package in.locationingooglemapv2;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class CircleTouchView extends View {

private static final String TAG = "CircleView";
private static final double MOVE_SENSITIVITY = 1.25;
private Paint circlePaint;
private boolean isPinchMode;
private int lastCircleX;
private int lastCircleY;
public Circle circle;
private boolean isDoneResizing = true;

public CircleTouchView(Context context) {
    super(context);
    setCirclePaint(0x220000ff);
}

public CircleTouchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setCirclePaint(0x220000ff);
}

public CircleTouchView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setCirclePaint(0x220000ff);
}

private void setCirclePaint(int color) {
    circle = new Circle();
    circlePaint = new Paint();
    circlePaint.setColor(color);
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawCircle(circle.centerX, circle.centerY, circle.radius, circlePaint);      
}

@Override
public boolean onTouchEvent(final MotionEvent event) {

    int historySize;
    double lastDistance;
    double oneBeforeLastDistance;

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            lastCircleX = circle.centerX;
            lastCircleY = circle.centerY;
            break;

        case MotionEvent.ACTION_POINTER_DOWN:
            isPinchMode = true;
            isDoneResizing = false;
            break;

        case MotionEvent.ACTION_MOVE:
            circle.centerX = lastCircleX;
            circle.centerY = lastCircleY;; 

            if (getTouchedCircle((int) event.getX(), (int) event.getY()) && !isPinchMode && isDoneResizing) {

                historySize = event.getHistorySize();
                if (historySize > 0) {

                    oneBeforeLastDistance = Math.sqrt((event.getX() - event.getHistoricalX(0, historySize - 1)) * 
                                                      (event.getX() - event.getHistoricalX(0, historySize - 1)) + 
                                                      (event.getY() - event.getHistoricalY(0, historySize - 1)) * 
                                                      (event.getY() - event.getHistoricalY(0, historySize - 1)));


                    if (oneBeforeLastDistance > MOVE_SENSITIVITY) {
                        circle.centerX = (int) event.getX();
                        circle.centerY = (int) event.getY();
                        lastCircleX = circle.centerX;
                        lastCircleY = circle.centerY;

                    }
                }
            }
            if (isPinchMode) {
                circle.centerX = lastCircleX;
                circle.centerY = lastCircleY;

                historySize = event.getHistorySize();
                if (historySize > 0) {

                    lastDistance = Math.sqrt((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) + 
                                             (event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1)));

                    oneBeforeLastDistance = Math.sqrt((event.getHistoricalX(0, historySize - 1) - event.getHistoricalX(1, historySize - 1)) * 
                                                      (event.getHistoricalX(0, historySize - 1) - event.getHistoricalX(1, historySize - 1)) + 
                                                      (event.getHistoricalY(0, historySize - 1) - event.getHistoricalY(1, historySize - 1)) * 
                                                      (event.getHistoricalY(0, historySize - 1) - event.getHistoricalY(1, historySize - 1)));


                    if (lastDistance < oneBeforeLastDistance) {
                        circle.radius -= Math.abs(lastDistance - oneBeforeLastDistance);
                    } else {
                        circle.radius += Math.abs(lastDistance - oneBeforeLastDistance);
                    }
                }
            }
            lastCircleX = circle.centerX;
            lastCircleY = circle.centerY;
            invalidate();
            break;

        case MotionEvent.ACTION_POINTER_UP:
            circle.centerX = lastCircleX;
            circle.centerY = lastCircleY;
            isPinchMode = false;
            break;

        case MotionEvent.ACTION_UP:
            circle.centerX = lastCircleX;
            circle.centerY = lastCircleY;
            isPinchMode = false;
            isDoneResizing = true;
            break;

        case MotionEvent.ACTION_CANCEL:
            break;

        case MotionEvent.ACTION_HOVER_MOVE:
            break;

        default:
            super.onTouchEvent(event);
            break;
    }
    return true;
}

private Boolean getTouchedCircle(final int xTouch, final int yTouch) {
    if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + 
        (circle.centerY - yTouch) * (circle.centerY - yTouch)   <= circle.radius * circle.radius) {
        return true;
    } else {
        return false;
    }

}

static class Circle {
    int radius;
    int centerX;
    int centerY;

    Circle() {
        this.radius = 150;
        this.centerX = 378;
        this.centerY = 478;
    }        
}
}

/*extends View {
    private static final int MODE_PINCH = 0;
    private static final int MODE_DONT_CARE = 1;

    ShapeDrawable mCircleDrawable;
    int mTouchMode = MODE_DONT_CARE;

    public CircleTouchView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mCircleDrawable = new ShapeDrawable(new OvalShape());
        mCircleDrawable.getPaint().setColor(0x66FFFFFF);
    }

    public CircleTouchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleTouchView(Context context) {
        this(context, null, 0);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            mCircleDrawable.setBounds(0, 0, 0, 0);
            invalidate();
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            prepareCircleDrawing(event);
            break;
        case MotionEvent.ACTION_MOVE:
            if (mTouchMode == MODE_PINCH) {
                prepareCircleDrawing(event);
            }
            break;
        case MotionEvent.ACTION_POINTER_UP:
            if (event.getActionIndex() <= 1) {
                mTouchMode = MODE_DONT_CARE;
            }
            break;
        default:
            super.onTouchEvent(event);
        }

        return true;
    }

    private void prepareCircleDrawing(MotionEvent event) {
        int top, right, bottom, left;
        int index = event.getActionIndex();

        if (index > 1) {
            return;
        }
        mTouchMode = MODE_PINCH;
        if (event.getX(0) < event.getX(1)) {
            left = (int) event.getX(0);
            right = (int) event.getX(1);
        } else {
            left = (int) event.getX(1);
            right = (int) event.getX(0);
        }

        if (event.getY(0) < event.getY(1)) {
            top = (int) event.getY(0);
            bottom = (int) event.getY(1);
        } else {
            top = (int) event.getY(1);
            bottom = (int) event.getY(0);
        }

        mCircleDrawable.setBounds(left, top, right, bottom);

        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mCircleDrawable.draw(canvas);
    }

}
*/

Thanks in advance

  • I dont recommend this approach because overriding pinch zoom gestures will conflict with the map gesture listeners. why dont you ask for circle radius before you draw it? – SMR May 30 '14 at 12:11
  • yes exactly its conflict with map gesture. how can i work with both? – user3690464 May 30 '14 at 12:13
  • please someone suggest me solution.! – user3690464 Jun 07 '14 at 13:20
  • not sure how to solve the conflict between touch event and map listener. its probably not the best solution but you can add CircleView on top of the map with transparent background and set its visibility to GONE, then , with a button or long press reveal the view and user will be able to expand/shrink – Guy S Jun 09 '14 at 11:05
  • can you please provide me any example? – user3690464 Jun 09 '14 at 13:27

1 Answers1

0

draw a circle with a radius or diameter of some size. Then allow the user to zoom in and out on the map. Once the user has finished zooming in or out to include areas in the circle, you can get the distance between the points on the circle.

You can either control the circle with pinch or control the map with pinch. It is much easier to draw a static circle and get the area than try to control both the map and the circle with gestures. There are only certain actions that are exposed by the map:

https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap