如何将Touch Listeners传递给自定义视图进行拖放操作?

时间:2012-06-21 11:23:36

标签: android ontouchlistener custom-view

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

Check This Image

我可以使用此代码

来提取这些点数
@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的上述字符一起拖放....

2 个答案:

答案 0 :(得分:7)

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

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

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

边界线

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

Resulting triangle with touch lines and intersections

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

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

您必须使用行tb和c和tc以及a执行此操作三次。每次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是您的点在同一行上的唯一解决方案 - &gt; 线性相关)!

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

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

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

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

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

我的解决方案明星案例:

  • 定义尖峰三角形

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

其他一切

使用三角形的外接圆将其三角化。通过找到定义不包含任何其他轮廓点的圆的三个点。所谓的Delaunay Triangulation。 让这个方法找到你的三角形。如果你有一些没有阴影的区域,你可以用属性“touchable”或类似的东西来定义一个三角形对象,让你的算法知道是否应该处理触摸。

如果您正确处理和更新三角形组,则不应再有关于如何处理凹形的问题。

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

  • 使用快速船体和三角测量方法。

  • 如果用户更改了积分位置,请更新三角形

  • 利润

答案 1 :(得分:3)

首先,您需要覆盖View.dispatchTouchEvent()以过滤掉三角形外触摸。查看my answer here有关如何操作的说明以及为什么需要使用该特定方法而不是OnTouchListener来使视图的某些部分触摸透明。

然后,在OnTouchListener中,您需要将触摸坐标与三角形点的坐标进行比较,以确定用户是否击中其中一个点或三角形内部。我还建议你在那里给点稍微边缘 - 在触摸屏上很难指出小物体,所以让用户就像是一个4-8像素的错误缓冲区。

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

如果你不熟悉拖放api,here's a nice tutorial。如果操作是ACTION_DOWN并且触摸点在三角形内,则需要从OnTouchListener调用startDrag()。

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

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