Android:如何检查矩形是不是包含触摸点?

Posted

技术标签:

【中文标题】Android:如何检查矩形是不是包含触摸点?【英文标题】:Android: How to check if a rectangle contains touched point?Android:如何检查矩形是否包含触摸点? 【发布时间】:2012-03-24 04:27:03 【问题描述】:

我会尝试开发一个可以绘制平面图的应用程序。因此,每个房间都有自己的 IDname,如果我触摸一个房间,我想显示带有该 ID 或名称的 Toast 消息。问题是如何检查是否以及哪个路径被触摸!

我看到很多关于这个问题的话题讨论。有人说要使用getBounds 方法,然后包含检查触摸点是否在Rect 中的方法。但是,我猜getBounds 返回包含路径的最小Rect,对吧?

因此,房间有不同的自定义几何形状,因此,如果我得到大约 2 个关闭房间的边界,方法可以返回一组共享的点。坏的!每个房间只有他们的面积分数。我该如何解决这个问题?

ios 中我可以使用 PathContainsPoint 方法,但不幸的是,android Path 没有类似的东西。

【问题讨论】:

【参考方案1】:

通过路径并检查路径上的位置是否靠近触摸点。以下方法适用于线性路径和复杂路径。

fun Path.doIntersect(x: Float, y: Float, width: Float): Boolean 
    val measure = PathMeasure(this, false)
    val length = measure.length
    val delta = width / 2f
    val position = floatArrayOf(0f, 0f)
    val bounds = RectF()
    var distance = 0f
    var intersects = false
    while (distance <= length) 
        measure.getPosTan(distance, position, null)
        bounds.set(
            position[0] - delta,
            position[1] - delta,
            position[0] + delta,
            position[1] + delta
        )
        if (bounds.contains(x, y)) 
            intersects = true
            break
        
        distance += delta / 2f
    
    return intersects

【讨论】:

【参考方案2】:

正如 Edward Falk 所说,最好的方法是使用 path.op(),因为 Region 是正方形的。一个点可以在 2 或 3 个区域中。

例如:

所有区域都会包含蓝点,但实际上只有 path4 包含这个点。

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;

【讨论】:

【参考方案3】:

Region 和 path.op() 方法运行良好,但缺乏精确度。经过一些测试,他们似乎在路径的段级别定义了一个区域,如下所示:image segment zone。另一种方法是使用polygons。虽然提供了更好的结果,但它仍然记录了一些意想不到的触摸,并错过了明显的触摸。但是还有另一种解决方案,它包括沿路径检索一组点,并在它们周围的区域内记录触摸。为此,我们可以使用PathMeasure 类来获取所需的点数,并将它们存储在List&lt;Float&gt; 中:

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]);

然后,我们需要遍历List&lt;Float&gt; 并检查触摸事件是否在围绕这些点的定义区域内。在这里,一个区域覆盖了路径长度的十分之一,因为我们收集了十个点:

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");
    


这些连续的区域以更高的精度捕获触摸事件,更接近路径:image points zone。 可以改进此方法,通过使用Rect 类来确保这些区域不相互重叠,并以更好的精度限制起点和终点。

【讨论】:

【参考方案4】:

这里有一个想法:创建一个新路径,它是围绕被触摸点的一个小正方形,然后使用 path.op() 将该路径与要测试的路径相交,看看结果是否为空。

【讨论】:

是的,但您仅限于 API 19 及更高版本。还有其他解决办法吗? 谢谢,我已经通过继承 Path 并跟踪所有点来解决它,然后我使用了这个 ***.com/a/16391873/3284364 公式并将其重写为 Java。 我想如果您愿意,我可以发布带有完整解决方案的答案。不知道这是否会帮助任何人 这个答案不起作用。也许这是预期的行为,也许是一个错误。我使用Path.op(__, Path.Op.INTERSECT) 来查看路径是否与以 (x, y) 为中心的小矩形相交,并且当 (x,y) 相对靠近路径但不在路径内部时,它有时会返回非空交叉点。注意我的路径是由直线组成的,有时它们会自行进入。另请注意,这不仅仅是因为我为op() 创建的“小矩形”不够小! @Bartando 我对您的解决方案很感兴趣,您可以发布答案吗?【参考方案5】:

好的,我解决了我的问题。我发布示例代码:

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;

【讨论】:

这会在路径周围而不是折线上的给定边界多边形中识别触摸事件。 正是我想要的 这不重要。我为我的目的保存了每个接触点 仅供参考,在 onDraw 中创建新的 RectF 和 Region 对性能不利,如果可能的话更好地重用。 我有另一条路径而不是事件 (x,y) 那么我如何检查我的一条路径是否在当前路径内?

以上是关于Android:如何检查矩形是不是包含触摸点?的主要内容,如果未能解决你的问题,请参考以下文章

Java检查两个矩形是不是在任何点重叠

检查一个触摸点是不是在 Unity 中的盒子碰撞器内

如何在蒙特卡洛积分中查找点x,y是不是在给定矩形内

如何在 Pygame 中检查变量是不是包含矩形? [复制]

Android Google Map如何检查用户是不是在标记矩形区域

检查 Android WebView 是不是正在使用触摸事件