I would try to develop an application in which I can draw a planimetry. So, each room has got its own ID or name and, if I touch a room, I want to show a T
Here's a thought: create a new path which is a tiny square around the point that was touched, and then intersect that path with your path to be tested using path.op() and see if the result is empty.
Ok i solved my problem. I post the example code:
Path p;
Region r;
@Override
public void onDraw(Canvas canvas) {
p = new Path();
p.moveTo(50, 50);
p.lineTo(100, 50);
p.lineTo(100, 100);
p.lineTo(80, 100);
p.close();
canvas.drawPath(p, paint);
RectF rectF = new RectF();
p.computeBounds(rectF, true);
r = new Region();
r.setPath(p, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
}
public boolean onTouch(View view, MotionEvent event) {
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
if(r.contains((int)point.x,(int) point.y))
Log.d(TAG, "Touch IN");
else
Log.d(TAG, "Touch OUT");
return true;
}
As Edward Falk say, best way is to use path.op() becouse Region is square. And one point can be in 2 or 3 Regions. For example: enter image description here All regions will contain blue point, but on fact only path4 contains this point.
int x, y;
Path tempPath = new Path(); // Create temp Path
tempPath.moveTo(x,y); // Move cursor to point
RectF rectangle = new RectF(x-1, y-1, x+1, y+1); // create rectangle with size 2xp
tempPath.addRect(rectangle, Path.Direction.CW); // add rect to temp path
tempPath.op(pathToDetect, Path.Op.DIFFERENCE); // get difference with our PathToCheck
if (tempPath.isEmpty()) // if out path cover temp path we get empty path in result
{
Log.d(TAG, "Path contains this point");
return true;
}
else
{
Log.d(TAG, "Path don't contains this point");
return false;
}
Region and path.op() approaches work well, but suffer from a lack of precision. After some tests, it seems like they define a zone at the segment level of the path, like so: image segment zone. Another approach is to work with polygons. While giving better results, it still registers some unexpected touches, and misses obvious ones. But there is another solution, which consists in retrieving a set of points along the path, and registering touches inside an area surrounding them. For that, we can use PathMeasure
class to get the desired number of points, and store them in a List<Float>
:
PathMeasure pathMeasure = new PathMeasure(path, false);
List<Float> listFloat = new ArrayList<>();
for (float i = 0; i < 1.1; i += 0.1) {
float[] pointCoordinates = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * i, pointCoordinates, null);
listFloat.add(pointCoordinates[0]);
listFloat.add(pointCoordinates[1]);
}
Then, we need to loop over the List<Float>
and check if a touch event is inside a defined zone surrounding these points. Here, a zone covers a tenth of the path length, since we collected ten points:
float areaLength = pathMeasure.getLength() / 20; // since we collect ten points, we define a zone covering a tenth of path length
for (int i = 0; i < listFloat.size() - 1; i += 2) {
if (touchX > listFloat.get(i) - areaLength
&& touchX < listFloat.get(i) + areaLength
&& touchY > listFloat.get(i+1) - areaLength
&& touchY < listFloat.get(i+1) + areaLength) {
Log.d(TAG, "path touched");
}
}
These successive zones capture touch events with a better precision, following more closely the path: image points zone.
This method can be improved, by working with Rect
class to ensure that these zones are not overlapping each other, and also to bound the start and end point with a better precision.