安卓 - 在谷歌地图上绘制可调整大小的圆形

6
我正在尝试在我的谷歌地图上绘制一个可调整大小的圆圈,用户可以使用触摸手势来展开或缩小它(例如,要缩小圆圈,用户将在屏幕上捏圆圈)。我希望它像地图中的放大/缩小选项一样工作,只是在地图上圆圈会变得更大/更小。是否有可能实现这一点?如果可以,我应该如何完成?
我在Google和Stackoverflow上搜索了一下,据我所知,我需要在我的地图片段上添加一个自定义视图,并实现OnTouchListener到该视图(这只是开始)。请问有人能给我建议或指导吗?我可以在地图上画一个圆圈,但我不知道如何让它响应触摸事件。
先行致谢。

我不确定我是否理解正确,当用户捏合时,圆圈会变大/缩小还是圆圈所包围的区域会放大/缩小?地图是否可平移?你到目前为止尝试了什么? - apenasVB
@VítordeAlmeida 我想在地图上画一个圆形,使用触摸手势来收缩/扩大该圆形,只有该圆形会收缩/扩大,而地图将保持与背景中的状态相同。目前,当我长按地图时,会弹出一个对话框,提示半径大小,然后在地图上绘制一个圆形,并尝试将其实现切换到问题中的实现方式。现在我正在尝试理解如何将地图片段与我的自定义视图叠加,这是实现我想要的内容的第一步(据我所知)。感谢您抽出时间来帮助。 - user2717267
3个回答

6
根据您的问题,您想要叠加一个“捏监听”视图,根据捏合绘制椭圆形状。我为此编写了一些未经充分测试的代码,请根据需要进行调整:
主布局:
<FrameLayout xmlns: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 -->
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="#F00" />

    <com.example.testapp.CircleTouchView
        android:id="@+id/circle_drawer_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

CircleTouchView:

public class CircleTouchView 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);
}
}

如果你想要一个完美的圆形而不是椭圆形,那么请修改prepareCircleDrawing()方法,使其在事件0和1之间取得最小的X和Y值。

注:您可以在调用 mCircleDrawable.setBounds(left, top, right, bottom); 之前添加下面的代码片段以绘制一个完美的圆。绘制圆的其他方式也有很多,这取决于您希望它表现的方式。

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

希望我表达清楚了,谢谢。

非常感谢,这正是我想要实现的,除了圆形部分。您能否再详细解释一下如何制作一个完美的圆形? - user2717267
已添加了一小段代码片段,请检查它是否符合您的需求。 - apenasVB
它确实画了一个圆,但是之前的实现比新的实现更加方便使用。我会继续努力改进它。 再次感谢您的帮助。 - user2717267
谢谢!这非常有帮助。 - davidforneron

2

这个问题被问了一段时间,但在使用圆形之外的东西之前,我过去曾经使用过它。

虽然不完美,但可能会帮助某些人。

public class CircleView 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 CircleView(Context context) {
    super(context);
    setCirclePaint(0x220000ff);
}

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

public CircleView(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;
    }        
}



}

2

这个问题被问了一段时间,但我想介绍另一种方法。我创建了自己的库来处理可拖动可调整大小的地图区域(圆形)。https://github.com/ac-opensource/MarkerBuilder

只需初始化MarkerBuilderManager即可实现。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接