6

我有一个关于处理的问题。我正在touch eventsCustomView自定义视图动态添加到布局(即 FrameLayout)。那些具有用于在角落拉点的 touchListener 的自定义视图(如下图所示)。除此之外,我必须在屏幕上拖放整个视图,如果用户触摸除了那些角点(图像中的颜色区域)以外的其他点,则必须拖放视图,否则,如果用户触摸之外查看我不想触发任何触摸监听器。

检查此图像

我可以通过使用此代码来提取这些点

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:

        if (topTouchArea.contains(event.getX(), event.getY())) {                
            currentTouch = TOUCH_TOP;
        } else if (RightTouchArea.contains(event.getX(),event.getY())) {                
            currentTouch = TOUCH_RIGHT;
        } else if (LeftTouchArea.contains(event.getX(),event.getY())) {            
            currentTouch = TOUCH_LEFT;
        } else {
            return false; //Return false if user touches none of the corners
        }
        return true; 
    case MotionEvent.ACTION_MOVE:

        switch (currentTouch) {
        case TOUCH_TOP:              
             top.x = event.getX();
             top.y = event.getY();                            
             invalidate();
             return true;
        case TOUCH_RIGHT:                
             Right.x = event.getX();
             Right.y = event.getY();                
             invalidate();
             return true;
        case TOUCH_LEFT:                 
             Left.x = event.getX();
             Left.y = event.getY();             
             invalidate();
             return true;       
        }         

    case MotionEvent.ACTION_UP:

        switch (currentTouch) {
        case TOUCH_TOP:
             top.x = event.getX();
             top.y = event.getY();                
             invalidate();
             currentTouch = NONE;
             return true;
        case TOUCH_RIGHT:
             Right.x = event.getX();
             Right.y = event.getY();             
             invalidate();
             currentTouch = NONE;
             return true;
        case TOUCH_LEFT:
            Left.x = event.getX();
             Left.y = event.getY();               
             invalidate();
             currentTouch = NONE;
             return true;      
        }         
        return false;
    }
    return false;
}

我怎样才能实现这种拖放以及CustomView的上述字符......

4

2 回答 2

7

我会使用一些线性代数来解决这个问题。您有 3 个或更多点不属于一条线(线性无关)。您可以使用此事实来确定接触点是否位于该区域内。您拥有的点越多,这就越困难,因此您需要将角数超过 3 的多边形转换为一组三角形,应用以下过程。

所以你有三个点 A,B,C 定义一个三角形。然后,您使用您的触摸点定义三条线以获得方向矢量 AT、BT、CT,而 A、B、Cs 表示这三条触摸线的基点。

然后用基点 A 定义边界线AB,用基点 A 定义 AC(A 或 C 是​​基点无关紧要),最后用基点 C 定义线 CB。所以让我们总结一下(大字母是表示向量,而小写字母表示标量或因子):

边界线

a:X=A+f1*AB
b:X=B+f2*BC
c:X=C+f3*AC

触摸线

ta:X=A+t1*AT
tb:X=B+t2*BT
tc:X=C+t3*CT

生成的带有接触线和交叉点的三角形

现在您将触摸线(图中粉红色)与与触摸线基点相对的边界线(绿色/红色)相交。例如,您需要将线“ta”与线 b 相交以获得交点 I3。如果将向量 AT 扩大因子t1 ,您将达到这一点。

如果该因子t1大于 1,则接触点 T 位于 A 和 I3 之间,即“ta”线。如果因子t1小于 1 T 位于线部分 A-I3 之外。

您将不得不对行 tb 和 c 以及 tc 和 a 执行此操作 3 次。每次 I(n) 点都需要小于 1(其中 n 在 {1,2,3} 中)。

如果此条件适用于所有三个交点,则您的接触点位于三角形内。

您将通过求解这些简单的方程组得到交集:

首先

A+t1*AT = B+f2*BC
<=>
0 = A+t1*AT - B - f2*BC

这个系统看起来像这样(其中 x,y 捐赠特定的坐标分量):

0 = xA + t1*(xT-xA) - xB - f2*(xC-xB)
0 = yA + t1*(yT-yA) - yB - f2*(yC-yB)

第二

B+t2*BT = C+f3*AC
<=>
0 = B+t2*BT - C - f3*AC

第三

C+t3*CT = A+f1*AB
<=>
0 = C+t3*CT - A - f1*AB

解决这三个系统将为您提供一个点是否位于该区域内的答案。解决方案必须与 0 明显不同(如果 0 是您的点在同一条线上的唯一解决方案 ->线性相关)!

基于此,您可以翻译对象的所有坐标。

如上所述,您需要缩减为三角形并将此方法应用于多边形的所有子三角形。如果一个匹配条件(或者如果你想实现多点触控,两个或更多)你的触摸就在对象区域内。

这只是三个小的计算,应该在“Touch-Down”事件处理程序中调用一次。由于您知道您触摸了该区域,因此无需在移动手指时进行所有这些计算。

关于三角化 这是一个更复杂的问题,不是一句话就能回答的。根据您对另一个答案的评论,您不想处理简单的多边形,而是想要处理由多边形组成的更复杂的形状。例如你的星形。在这里你可以使用上面的方法,你需要自己定义你的三角形。因为要知道哪个角属于一张脸,哪个角不属于这并不是那么简单。

另一种解决方案是使用所谓的 Quick Hull 算法从点集生成凸包。这将为您提供形状的轮廓,您应该使用哪些角点进行三角化。如果你有一些“无阴影”的面,如果你真的想处理所有可能的点集,也只需处理它们。

我在你的明星案例中的解决方案:

  • 定义你的尖峰三角形

  • 使用我前面提到的方法(通过解决一些 LSE)来确定是否被触摸。

对于其他一切

使用三角形的外接圆对其进行三角化。通过找到定义一个不包含任何其他轮廓点的圆的三个点。所谓的德劳内三角测量。让这个方法找到你的三角形。如果您有一些未着色的区域,您可以定义一个具有“可触摸”属性或类似属性的三角形对象,让您的算法知道是否应该处理触摸。

如果你正确地处理和更新你的三角形集,就不应该再有关于如何处理凹形的问题了。

对于您的五角大楼,请尝试以下操作:

  • 使用快速外壳和三角测量方法。

  • 如果用户更改点位置,则更新三角形

  • 利润

于 2012-06-26T10:20:37.753 回答
3

首先,您需要重写 View.dispatchTouchEvent() 以过滤掉三角形外的触摸。在此处查看我的答案,以了解如何执行此操作以及为什么需要使用该特定方法而不是 OnTouchListener 来使部分视图触摸透明。

然后,在您的 OnTouchListener 中,您需要将触摸坐标与三角形点的坐标进行比较,以确定用户是点击了其中一个点还是三角形的内部。我还建议您在此处给点留出一点余地-在触摸屏上指向小对象非常困难,因此让用户为此设置一个 4-8 像素的错误缓冲区。

因此,如果用户击中一个点 - 拖动该点,如果他们击中三角形内部而不是其中一个点(即触摸点在三角形内但不在某个点的位置之一内) - 拖动整个看法。此外,如果三角形太小以至于点边距重叠(这将导致触摸匹配多个点位置),则只需选择最近的一个。

如果您不熟悉拖放 api,这里有一个不错的教程。如果动作是 ACTION_DOWN 并且触摸点在三角形内,则需要从 OnTouchListener 调用 startDrag()。

对于 pre-honeycomb API,有一个名为android-dragarea的拖放库。文档中有指向示例应用程序的链接。

UPD哦,我没有意识到你也在寻找算法。您想要一个 Point-in-Polygon 算法,这里有一些不错的解决方案,它们几乎适用于任何复杂的多边形

  • 光线投射算法实现1:http ://chandan-tech.blogspot.com/2010/12/whether-point-lies-inside-polygon-2d.html (有完整代码,马上就可以了)
  • 光线投射算法实现 2:这是第一个的稍微不同的实现:http ://www.anddev.org/other-coding-problems-f5/using-java-awt-polygon-in-android-t6521.html (完整的代码,立即工作)
  • 这个实现起来有点复杂,需要更多的 RAM,但我相信它可能比前两个在速度上更有优势,因为它是基于位图的:https ://stackoverflow.com/a/2597702/375929
于 2012-06-26T09:57:18.443 回答