我有一个关于touch events
处理CustomView
的问题。我正在动态地将自定义视图添加到布局(即FrameLayout)。那些具有touchListeners的自定义视图用于在角落处拉点(如下图所示)。除此之外我必须在屏幕上拖放整个视图,如果用户触摸除了那些角点之外(图像中的颜色区域)必须拖放视图,否则不会,以及如果用户触摸到外面的那个查看我不想触发任何触摸侦听器。
我可以使用此代码
来提取这些点数@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的上述字符一起拖放....
答案 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
现在,您将触摸线(图中的粉红色)与触摸线基点相反的边界线(绿色/红色)相交。例如,您需要将线“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算法,这里有一些不错的解决方案,它们适用于几乎任何复杂的多边形: