Here how I made it. I have a list of object that are drawn on my canvas
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
for (DrawObject drawObject : mDrawObjects) {
drawObject.draw(canvas);
}
}
On the onTouchevent, I iterate on this list, passing the motion event coordinate :
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());
}
My drawObject is a line, so it contains a starting point, and an ending point.
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);
}
}
Here I used the answer found in this topic to know if the touch is in the line bound. You need to use the width of your line as epsilon to allow the user to click outside of the center (line) of it.
There is one last problem :
With this method, if you click outside of the drawn line, but still in its continuity, it will trigger the onTouch. To fix this problem, we will use the rect that I drew in blue.
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);
}
On the touchup of a drawObject, we create the rect, sort it (so the right is really on the right side of the rectangle), and then we calculate a slightly larger rect than the drawn line.
And here the method that I call to check the touch :
public boolean isInBounds(float x, float y) {
if (mRectF.contains(x, y)) {
return isPointOnLine(mStart, mEnd, new PointF(x, y));
}
return false;
}
Here is the full class that I made :
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;
}
}