Skip to content Skip to sidebar Skip to footer

Android - Draw Resizable Circle On Google Map

I'm trying to draw a resizable circle on top of my google map, which the user will be able to expand or shrink using touch gestures (for example to shrink the circle the user will

Solution 1:

Based on your question, you want to overlay a "pinch listening" view that draws an oval shape based on the pinch. I made some poorly-tested code for this purpose, adapt it as you need:

MainLayout:

<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><!-- Replace the ImageView with your MapView or whatever you are 
         overlaying with the oval shape --><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:src="#F00" /><com.example.testapp.CircleTouchViewandroid:id="@+id/circle_drawer_view"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout>

CircleTouchView:

publicclassCircleTouchViewextendsView {
privatestaticfinalintMODE_PINCH=0;
privatestaticfinalintMODE_DONT_CARE=1;

ShapeDrawable mCircleDrawable;
intmTouchMode= MODE_DONT_CARE;

publicCircleTouchView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    mCircleDrawable = newShapeDrawable(newOvalShape());
    mCircleDrawable.getPaint().setColor(0x66FFFFFF);
}

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

publicCircleTouchView(Context context) {
    this(context, null, 0);
}

@OverridepublicbooleanonTouchEvent(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);
    }

    returntrue;
}

privatevoidprepareCircleDrawing(MotionEvent event) {
    int top, right, bottom, left;
    intindex= 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();
}

@OverrideprotectedvoidonDraw(Canvas canvas) {
    mCircleDrawable.draw(canvas);
}
}

If you want a perfect circle instead of an oval shape, change the prepareCircleDrawing() method so that it takes the smallest values for X and Y between event 0 and 1.

Edit: you can add the snippet below before calling mCircleDrawable.setBounds(left, top, right, bottom); to draw a perfect circle. There are other ways for drawing circles, it depends on how you want it to behave.

int height = bottom - top;
int width = right - left;

if (height > width) {
    int delta = height - width;
    top += delta / 2;
    bottom -= delta / 2;
} else {
    int delta = width - height;
    left += delta / 2;
    right -= delta / 2;
}

Hope I made myself clear, regards.

Solution 2:

it has been a while since the question was asked but I used this in the past before switching to something different than a circle.

its not perfect but maybe it will help someone.

publicclassCircleViewextendsView {

privatestaticfinalStringTAG="CircleView";
privatestaticfinaldoubleMOVE_SENSITIVITY=1.25;
private Paint circlePaint;
privateboolean isPinchMode;
privateint lastCircleX;
privateint lastCircleY;
public Circle circle;
privatebooleanisDoneResizing=true;

publicCircleView(Context context) {
    super(context);
    setCirclePaint(0x220000ff);
}

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

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

privatevoidsetCirclePaint(int color) {
    circle = newCircle();
    circlePaint = newPaint();
    circlePaint.setColor(color);
}

@OverrideprotectedvoidonDraw(Canvas canvas) {
    canvas.drawCircle(circle.centerX, circle.centerY, circle.radius, circlePaint);      
}

@OverridepublicbooleanonTouchEvent(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;

    }
    returntrue;
}

private Boolean getTouchedCircle(finalint xTouch, finalint yTouch) {
    if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + 
        (circle.centerY - yTouch) * (circle.centerY - yTouch)   <= circle.radius * circle.radius) {
        returntrue;
    } else {
        returnfalse;
    }

}

staticclassCircle {
    int radius;
    int centerX;
    int centerY;

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



}

Solution 3:

It has been a while since this question was asked but I want to introduce another way of doing this. I created my own library to handle draggable resizable map area (circle). https://github.com/ac-opensource/MarkerBuilder

It can be implemented by just initialising the MarkerBuilderManager

markerBuilderManager = new MarkerBuilderManagerV2.Builder(this)
          .map(mMap) // required
          .build();

Post a Comment for "Android - Draw Resizable Circle On Google Map"