Search code examples
androidimageviewontouchlistener

Move Imageview circular


I'm trying to code an app, where I'm able to move an Imageview only on a specific circular path.

@Override
public boolean onTouch(View view, MotionEvent event) {

    float touchedX = 0;
    float touchedY = 0;
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:  

            break;
        case MotionEvent.ACTION_UP:
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            break;
        case MotionEvent.ACTION_POINTER_UP:
            break;
        case MotionEvent.ACTION_MOVE:

            touchedX = event.getX();
            touchedY = event.getY();
            float deltaX = touchedX-widthHalf;
            float deltaY = touchedY-heightHalf;
            double angle = Math.atan(deltaY/deltaX);                        
            timeCircleButton.setX(widthHalf + ((float)(radius*(Math.cos(angle)))));
            timeCircleButton.setY(heightHalf + ((float)(radius*(Math.sin(angle)))));


            break;
    }

    return true;
}

This is my onTouchListener so far. HeightHalf and WidthHalf are the Coord. of the center point. I think my idea is right, but right now the ImageView is moving on a part of the circle and not on the whole circle like I want to.

So my question: Where is my fault?

If you need more information please let me know.


Solution

    • The problem is that the arctan function values are in (-pi/2, pi/2)

    This is a mathematical problem, the java implementation follows the maths. The tangent function is by default not invertible, but it can be inverted if you restrict the domain to (-pi/2, pi/2). Therefore the inverse function arctan will only gives back values in (-pi/2, pi/2). The java implementation atan pushes this idea further defining this method also at +/-infinity (which mathematically aren't real numbers). As a result you see your ImageView moving only on the right-hand side of the circle.

    Solution: use Math.hypot() to calculate the hypotenuse then calculate sine and cosine by divisions.

    touchedX = event.getX();
    touchedY = event.getY();
    
    double deltaX = touchedX - widthHalf;
    double deltaY = touchedY - heightHalf;
    double hypot = Math.hypot(deltaX, deltaY);
    double cosine = deltaX / hypot;
    double sine = deltaY / hypot;  
    
    timeCircleButton.setX((float)(widthHalf + radius * cosine));
    timeCircleButton.setY((float)(heightHalf + radius * sine)));
    

    EDIT: I coded up a small example with this code and it works. You can find it here.