如何从Android画布中获取触摸形状(弧形)?

Jor*_*dez 5 android android-custom-view android-canvas

我正在构建一个自定义视图。

它有4条弧线。

我正在使用RectF形状绘制弧线,期间onDraw()

// arcPaint is a Paint object initialized
// in the View's constructor
arcPaint.setStrokeWidth(strokeWidth);
arcPaint.setColor(arcColor);

// Draw arcs, arc1, arc2, arc3 & arc4 are
// measured and initialized during onMeasure()
canvas.drawArc(arc1, startAngle, arc1Angle, false, arcPaint);
canvas.drawArc(arc2, startAngle, arc2Angle, false, arcPaint);
canvas.drawArc(arc3, startAngle, arc3Angle, false, arcPaint);
canvas.drawArc(arc4, startAngle, arc4Angle, false, arcPaint);
Run Code Online (Sandbox Code Playgroud)

所以,结果是:

在此输入图像描述

如果我RectF使用canvas.drawRect()以下代码绘制对象:

canvas.drawRect(arc1, arcPaint);
canvas.drawRect(arc2, arcPaint);
canvas.drawRect(arc3, arcPaint);
canvas.drawRect(arc4, arcPaint);
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

在此输入图像描述

将圆弧和矩形绘制在一起,得出:

在此输入图像描述

我知道我可以重写该onTouchEvent()方法并获取 X、Y 坐标,但我不知道每个坐标如何与画布中绘制的每个形状相关。

我想做的是检测何时触摸 内的圆弧而不是矩形canvas,我怎样才能实现这一点?

编辑:

我的意思是,我不想检测用户何时触摸这些区域(矩形的角):

在此输入图像描述

Mik*_* M. 5

这基本上可以归结为比较距该图形中心点的距离。由于边界矩形始终是正方形,因此这些距离之一基本上是固定的,这使得这变得更容易。

这里的一般方法是计算从中心点到触摸点的距离,并检查该距离与圆弧半径的差值是否在笔划宽度的一半以内。

private static boolean isOnRing(MotionEvent event, RectF bounds, float strokeWidth) {
    // Figure the distance from center point to touch point.
    final float distance = distance(event.getX(), event.getY(),
                                    bounds.centerX(), bounds.centerY());

    // Assuming square bounds to figure the radius.
    final float radius = bounds.width() / 2f;

    // The Paint stroke is centered on the circumference,
    // so the tolerance is half its width.
    final float halfStrokeWidth = strokeWidth / 2f;

    // Compare the difference to the tolerance.
    return Math.abs(distance - radius) <= halfStrokeWidth;
}

private static float distance(float x1, float y1, float x2, float y2) {
    return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
Run Code Online (Sandbox Code Playgroud)

在该onTouchEvent()方法中,isOnRing()只需使用MotionEvent、圆弧的边界RectFPaint的笔划宽度来调用。例如:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (isOnRing(event, arc1, arcPaint.getStrokeWidth())) {
                // Hit.
            }
            break;
            ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

这本身只能确定触摸点是否位于由半径和笔画宽度描述的完整环上的任何位置。也就是说,它没有考虑弧线中的“间隙”。如果需要的话,有多种方法可以处理这个问题。

如果您想让起始角和扫描角与 x/y 轴齐平,最简单的方法可能是检查触摸点和中心点坐标差异的符号。从广义上讲,如果触摸 x减去中心 x为正,则您位于右侧;反之则为正。如果为负,则您在左侧。同样,计算顶部或底部将允许您确定触摸发生在哪个象限,以及是否忽略该事件。

final float dx = event.getX() - arc1.centerX();
final float dy = event.getY() - arc1.centerY();

if (dx > 0) {
    // Right side
}
else {
    // Left side
}
...
Run Code Online (Sandbox Code Playgroud)

然而,如果任一角度不与轴共端,那么数学就会稍微复杂一些。例如:

private static boolean isInSweep(MotionEvent event, RectF bounds,
                                 float startAngle, float sweepAngle) {
    // Figure atan2 angle.
    final float at =
        (float) Math.toDegrees(Math.atan2(event.getY() - bounds.centerY(),
                                          event.getX() - bounds.centerX()));

    // Convert from atan2 to standard angle.
    final float angle = (at + 360) % 360;

    // Check if in sweep.
    return angle >= startAngle && angle <= startAngle + sweepAngle;
}
Run Code Online (Sandbox Code Playgroud)

if然后,in将onTouchEvent()更改以合并这两项检查。

if (isOnRing(event, arc1, arcPaint.getStrokeWidth()) &&
    isInSweep(event, arc1, startAngle, arc1Angle)) {
    // Hit.
}
Run Code Online (Sandbox Code Playgroud)

这些方法可以很容易地合并为一个,但为了清楚起见,这里将它们分开。