6

I've a question concerning handling touch events for CustomView.I'm adding custom view's dynamically to layout (i.e FrameLayout). Those custom views having touchListeners for pulling points at corners (It shows in the below image). Along with that i have to drag and drop the total view on the screen, if user touches other than those corner points (color area in the image) have to drag and drop of the view otherwise not, and also if user touches outside of that view i don't want trigger any touch listeners.

Check This Image

I am able to pull those points by using this code

@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;
}

How can i achieve this drag and drop along with above characters of the CustomView....

RajaReddy PolamReddy
  • 22,428
  • 19
  • 115
  • 166

2 Answers2

7

I'd use some linear algebra to solve this problem. You have 3 or more points which are not part of a line (linear independent). You could use this fact to determine if the touch point lies within the area. This gets more difficult the more points you have, So you need to transform polygons with more corners than 3 down to a set of triangles, applying the following procedure.

So you have three points A,B,C defining a triangle. Then you define three lines using your touch point to get the direction vectors AT,BT,CT while the A,B,Cs denote the base points of these three touch lines.

Then you define boundary lines AB with the base point A, AC with base point A (it does not matter if A or C is the base point) and at last the line CB with the base point C. So lets summarize (large letters are denoting vectors while small letters are denoting scalars or factors):

Boundary lines:

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

Touch lines:

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

Resulting triangle with touch lines and intersections

Now you intersect the touch lines (pink in in the figure) with the border lines (green/red) in opposite to base point of the touch line. For example you need to intersect line 'ta' with line b to get intersection point I3. This point you'll reach if you extent the vector AT by the factor t1.

If this factor t1 is greater than 1 the touch point T lies between A and I3 one the line 'ta'. if The factor t1 is lesser than 1 T lies outside of the line part A-I3.

You will have to do this three times with line tb and c and tc and a. Each time the I(n) points will need to be lesser than one (where is n is in {1,2,3}).

If this condition applies for all three intersections your touch point is inside the triangle.

You will get the Intersection by solving these simple systems of equations:

The first

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

This systems looks like this (where x,y donate the specific coordinate component):

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

The second

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

The third

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

Solving these three systems will give you the answer if a point lies within the area or not. The solution needs to be distinctly from 0 (If 0 is the only solution your points are on the same line -> linear dependent)!

Based upon that you can translate your all of the coordinates of your object.

As mentioned above you'll need to reduce down to triangles and apply this method to all subtriangles of a polygon. If one matches the condition (or two or more if you want to implement multitouching) your touch is within the object area.

This are just three small computations and should be called in the "Touch-Down" Event Handler once. Since you know your you touched the area there is no need to do all these computations while moving your finger(s).

About Trianglization This is a more complex matter not answered in one sentence. According to your comments on the other answer you do not want to handle simple polygons, you want to handle more complex shapes consisting of polygons. As for example your star shape. Here you can just use the method above, you need to define your triangles yourself. Because its not that simple to know which corner belongs to a face and which not.

An alternate solution would be using the so called Quick Hull algorithm to generate a convex hull from a point set. This will give you the contour of the shape which corner points you should use to trianglelize. If you have some "unshaded" faces just handle them as touched as well, if you really want to handle all possible point sets.

My Solution in you star case:

  • Define you spike triangles

  • use the method I mentioned earlier above (by solving some LSEs) to determine if one is touched or not.

For everything else:

Trianglize it by using circumcircle of a triangle. By finding three points which defining a circle not containing any other contour points. The so called Delaunay Triangulation. Let this method find your triangles. If you have some unshaded areas you could define a triangle object with the property "touchable" or something like that, to let your algorithm know if the touch should be handled or not.

If you handle and update you set of triangles properly there should be no more questions about how to handle a concave shape.

For your pentagon try following:

  • use the the quick hull and triangulation method.

  • if a user changes a points position, update triangles

  • profit

kneo
  • 396
  • 1
  • 13
  • I am able to get the touch point if it is in convex, if it is in concave i am unable get the correct touch point... – RajaReddy PolamReddy Jun 29 '12 at 06:15
  • Thats why you need to break down a complex polygon into a set of triangles. I mentioned it in my post. It would need another post of this length to explain how to "trianglelize" your polygon. I'll look into it as soon as possible. Adding the changes to my post above. – kneo Jun 29 '12 at 06:34
  • if user change my polygon in-to Star at that time even i was takes as three triangles, touch area goes to non-touch area also... – RajaReddy PolamReddy Jun 29 '12 at 06:38
  • Try following: You have a set of n points. Take one point and compute the distances to all other points. Take the point with the smallest distance and define a line Again compute all distances between the line and all (points) Again take the point with the smallest distance to the line. So you got your first triangle. Now define a line with your last point and the point before that compute distances ... there is more to think about here you should take a piece of paper and draw the situation. But as I said, I'm busy right now, so I can't complete the post right now. Later than. – kneo Jun 29 '12 at 06:48
  • for triangle i am able to find touch point was inside or not, i am facing problem for translation.... – RajaReddy PolamReddy Jun 29 '12 at 06:51
  • you just need to compute the difference vector of your start touch point and your current "onMove" touch point. this difference is added to all you triangle points. When done, update your starting touch point to be the current touch point. – kneo Jun 29 '12 at 06:59
  • Yes, i did like you said but forget the update the touch point, Now it's working for Triangle, Thank you. i have to test it for Square, Circle, Line and Pentagon.. – RajaReddy PolamReddy Jun 29 '12 at 07:05
  • I'll answer your polygon question as soon as I have a little spare time ... so stay tuned ;) – kneo Jun 29 '12 at 07:11
3

First of all, you need to override View.dispatchTouchEvent() to filter out-of-triangle touches. Check out my answer here for explanation on how to do that and why you need to use that particular method and not OnTouchListener to make parts of the view touch-transparent.

Then, in your OnTouchListener, you'll want to compare touch coordinates to the ones of your triangle points to figure out whether user hit one of the points or the inside of the triangle. I also suggest you give the points a little margin there - it's pretty hard to point little objects on touch screens, so let the user have like an 4-8 pixel mistake buffer for that.

So, if the user hits a point - drag the point, if they hit the inside of the triangle and not one of the points (i.e. touch point is within the triangle but is not within one of the points' localities) - drag the whole view. Also, If the triangle is so small that point margins overlap (which will result in a touch matching more than one point locality), just pick the closest one.

In case you're not familiar with drag-and-drop api, here's a nice tutorial. You'll need to call startDrag() from your OnTouchListener if the action is ACTION_DOWN and the touch point is inside the triangle.

For pre-honeycomb APIs, there's a drag-and-drop library called android-dragarea. There's a link to example app in documentation.

UPD Oh, I didn't realize you were looking for algorithms also. You want a Point-in-Polygon algorithm , here are some nice solutions, they'll work for almost any complex polygon:

Community
  • 1
  • 1
Ivan Bartsov
  • 19,664
  • 7
  • 61
  • 59
  • Actually that Drag and drop will work from android 4.0, but i have to support my app from 2.2 onward's.... – RajaReddy PolamReddy Jun 26 '12 at 10:14
  • There's a library that mimics honeycomb's dnd, I updated the answer, see the link. – Ivan Bartsov Jun 26 '12 at 10:30
  • Hey I noticed you're having lengthy discussions on point-in-polygon algorithms in kneo's answer, check out my updated answer on algorithms. No need to think triangles at all, this problem is quite a popular one in graphics, it's been solved many times, lots of solutions available. – Ivan Bartsov Jun 29 '12 at 10:04
  • One of the algorithm working for finding a point is existing inside the polygon or not. but i facing this problem : check this image . https://dl.dropbox.com/u/38493970/newone.png if you did't understand let me know.. – RajaReddy PolamReddy Jun 29 '12 at 11:39
  • For this case you can treat the inner pentagon formed by triangle sides as a separate polygon (for alg 1 & 2). Then just add it to the condition with OR (like so `if (hitsOneOfTheTriangles || hitsThePentagon) {weeeITouchedTheThingLetsStartDraggin();}`) And you can paint the pentagon pixels with the "hit" color values on the determination bitmap for alg 3. This gets trickier if that pentagon has been formed by triangles in runtime (by user) and you can have any other similar construction as well - you'll have to see what triangles form other objects (from point coordinates) and keep lists. – Ivan Bartsov Jun 29 '12 at 11:54
  • Ya other than this ( polygon converted to star i am unable get touch listeners at center ) i am thinking have to kept that or not in the requirement. – RajaReddy PolamReddy Jul 02 '12 at 09:41
  • You can use dispatchTouchEvent or touch listener of the parent to handle the center pentagon. You'll have to make up your own pseudo-polygon-things though. Like, run through all points of all objects and determine which points overlap, build overlap graph, find cycles - those will be the objects formed by other objects' sides. Then in listener (or dispatchTouchEvent) check whether touch point hits one of them - using ray casting or bitmap. – Ivan Bartsov Jul 02 '12 at 09:50
  • Hah ) Well, that would be a judgement call. – Ivan Bartsov Jul 02 '12 at 10:42