我制作了一个应用程序,允许用户在自定义视图上进行指纹识别。但他只能画直线,宽线。问题是使用
canvas.drawLine(x, y, end.x, end.y, paint);
画一条线,我们无法检测线上的触摸。 我尝试使用Rect作为触摸区域,但是Rect是一个直的矩形,它不能与行区域匹配:
没有找到使用路径的方法,因为它太窄而无法点击它。
答案 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