检测画布上的线条的TouchEvent

时间:2015-03-11 08:45:55

标签: android canvas touch line

我制作了一个应用程序,允许用户在自定义视图上进行指纹识别。但他只能画直线,宽线。问题是使用

canvas.drawLine(x, y, end.x, end.y, paint);

画一条线,我们无法检测线上的触摸。 我尝试使用Rect作为触摸区域,但是Rect是一个直的矩形,它不能与行区域匹配:

In red the line draw by the user, in blue the rect that matches the touch zone

没有找到使用路径的方法,因为它太窄而无法点击它。

3 个答案:

答案 0 :(得分:1)

我是怎么做到的。我有一个在画布上绘制的对象列表

@Override
protected void onDraw(final Canvas canvas) {
    super.onDraw(canvas);
    for (DrawObject drawObject : mDrawObjects) {
        drawObject.draw(canvas);
    }
}

在onTouchevent上,我迭代这个列表,传递运动事件坐标:

ListIterator iterator = mDrawObjects.listIterator(mDrawObjects.size());
iterator.previous();
while (iterator.hasPrevious()) {
    final DrawObject drawObject = (DrawObject) iterator.previous();
    boolean isInBound = drawObject.isInBounds(motionEvent.getX(), motionEvent.getY());
}

我的drawObject是一条线,因此它包含一个起点和一个终点。

public boolean isPointOnLine(PointF lineStaPt, PointF lineEndPt, PointF point) {
    final float EPSILON = width;
    if (Math.abs(lineStaPt.x - lineEndPt.x) < EPSILON) {
        // We've a vertical line, thus check only the x-value of the point.
        return (Math.abs(point.x - lineStaPt.x) < EPSILON);
    } else {
        float m = (lineEndPt.y - lineStaPt.y) / (lineEndPt.x - lineStaPt.x);
        float b = lineStaPt.y - m * lineStaPt.x;
        return (Math.abs(point.y - (m * point.x + b)) < EPSILON);
    }
}

在这里,我使用了此topic中的答案来了解触摸是否在行界限中。您需要将线条的宽度用作epsilon,以允许用户在其中心(线)之外单击。

最后一个问题是:

使用此方法,如果单击绘制线的外部,但仍处于连续状态,则会触发onTouch。要解决这个问题,我们将使用我用蓝色绘制的矩形。

public void touchUp(float x, float y) {
    mRectF.set(mStart.x, mStart.y, mEnd.x, mEnd.y);
    mRectF.sort();
    int left = (int) ((mRectF.left < mRectF.right ? (int) mRectF.left : (int) mRectF.right) - width / 2);
    int right = (int) ((mRectF.right > mRectF.left ? (int) mRectF.right : (int) mRectF.left) + width / 2);
    int top = (int) ((mRectF.top < mRectF.bottom ? (int) mRectF.top : (int) mRectF.bottom) - width / 2);
    int bottom = (int) ((mRectF.bottom > mRectF.top ? (int) mRectF.bottom : (int) mRectF.top) + width / 2);
    mRectF.set(left, top, right, bottom);
}

在drawObject的touchup上,我们创建了rect,对它进行排序(所以右边实际上是在矩形的右侧),然后我们计算一个比绘制线略大的矩形。

这里是我打电话检查触摸的方法:

public boolean isInBounds(float x, float y) {
    if (mRectF.contains(x, y)) {
        return isPointOnLine(mStart, mEnd, new PointF(x, y));
    }

    return false;
}

这是我完整的课程:

public class LabelObject implements DrawObject {
private static final String TAG = LabelObject.class.getSimpleName();
protected static final boolean DEBUG = true;
private PointF mStart, mEnd;
private Paint mPaint;
private RectF mRectF;
private float width = 60;

private static final int MIN_SIZE = 40;

public LabelObject() {
    mPaint = new Paint();
    mPaint.setStrokeWidth(width);
    mPaint.setColor(Color.RED);

    mStart = new PointF();
    mEnd = new PointF();
    mRectF = new RectF();
}

public void draw(Canvas canvas) {
    canvas.drawLine(mStart.x, mStart.y, mEnd.x, mEnd.y, mPaint);
}

public void touchStart(float x, float y) {
    mStart.set(x, y);
    mEnd.set(x, y);
}

public void touchMove(float x, float y) {
    mEnd.set(x, y);
}

public void touchUp(float x, float y) {
    mRectF.set(mStart.x, mStart.y, mEnd.x, mEnd.y);
    mRectF.sort();
    int left = (int) ((mRectF.left < mRectF.right ? (int) mRectF.left : (int) mRectF.right) - width / 2);
    int right = (int) ((mRectF.right > mRectF.left ? (int) mRectF.right : (int) mRectF.left) + width / 2);
    int top = (int) ((mRectF.top < mRectF.bottom ? (int) mRectF.top : (int) mRectF.bottom) - width / 2);
    int bottom = (int) ((mRectF.bottom > mRectF.top ? (int) mRectF.bottom : (int) mRectF.top) + width / 2);
    mRectF.set(left, top, right, bottom);
}

public boolean isPointOnLine(PointF lineStaPt, PointF lineEndPt, PointF point) {
    final float EPSILON = width;
    if (Math.abs(lineStaPt.x - lineEndPt.x) < EPSILON) {
        // We've a vertical line, thus check only the x-value of the point.
        return (Math.abs(point.x - lineStaPt.x) < EPSILON);
    } else {
        float m = (lineEndPt.y - lineStaPt.y) / (lineEndPt.x - lineStaPt.x);
        float b = lineStaPt.y - m * lineStaPt.x;
        return (Math.abs(point.y - (m * point.x + b)) < EPSILON);
    }
}

public boolean isInBounds(float x, float y) {
    if (mRectF.contains(x, y)) {
        return isPointOnLine(mStart, mEnd, new PointF(x, y));
    }

    return false;
}

public boolean isLargeEnough() {
    return Math.abs(mRectF.height()) > MIN_SIZE || Math.abs(mRectF.width()) > MIN_SIZE;
}
}

答案 1 :(得分:0)

但是对于不同长度的线,触摸差异很大

例如,在小线附近触摸会产生16,而在距离线相同距离的大线附近触摸会产生143.

我使用以下公式

Math.abs(touchY - (m * touchX + b).

您是否找到了此

的替代解决方案

答案 2 :(得分:0)

我的建议是计算单击点(x0,y0)与直线之间的距离d。您可能要使用公式here(由两点定义的线)。 然后,使距离d小于epsilon(可能是线的宽度)。 The Formula