【问题标题】: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 没有类似的东西。

【问题讨论】:

    标签: android path drawable contains android-canvas


    【解决方案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 并跟踪所有点来解决它,然后我使用了这个 stackoverflow.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) 那么我如何检查我的一条路径是否在当前路径内?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-03-16
          • 2012-02-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多