Android - Draw Resizable Circle On Google Map
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"