Drag events

Now that we're already reacting to ACTION_DOWN and ACTION_UP events, we will add a bit more complexity by reacting to ACTION_MOVE as well.

Let's update the angle, based on the amount of dragging in both directions. To do so, we need to store where the user pressed in the first place, so we'll store the variables lastX and lastY with the X and Y coordinates on the ACTION_DOWN event.

When we receive an ACTION_MOVE event, we calculate the difference between the lastX and lastY coordinates and the current values we received with the event. We update selectedAngle with the average of the X and Y difference, and we finally update the lastX and lastY coordinates. We have to remember to call invalidate or otherwise our view will not be redrawn:

private float lastX, lastY; 
 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
    switch(event.getAction()) { 
        case MotionEvent.ACTION_DOWN: 
            changePressedState(true); 
 
            lastX = event.getX(); 
            lastY = event.getY(); 
            return true; 
 
        case MotionEvent.ACTION_UP: 
            changePressedState(false); 
            return true; 
 
        case MotionEvent.ACTION_MOVE: 
            float dragX = event.getX(); 
            float dragY = event.getY(); 
 
            float dx = dragX - lastX; 
            float dy = dragY - lastY; 
 
            selectedAngle += (dx + dy) / 2; 
 
            lastX = dragX; 
            lastY = dragY; 
 
            invalidate(); 
            return true; 
 
        default: 
            return false; 
    } 
} 

That movement might feel a bit unnatural, so if we want the angle of the circle to follow where we actually pressed, we should change from Cartesian coordinates to polar coordinates:
https://en.wikipedia.org/wiki/List_of_common_coordinate_transformations.

With this change, there is no need to track the previous coordinates, so we can replace our code with the following:

private int computeAngle(float x, float y) { 
    x -= getWidth() / 2; 
    y -= getHeight() / 2; 
 
    int angle = (int) (180.0 * Math.atan2(y, x) / Math.PI) + 90; 
    return (angle > 0) ? angle : 360 + angle; 
} 
 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
    switch(event.getAction()) { 
        case MotionEvent.ACTION_DOWN: 
            selectedAngle = computeAngle(event.getX(), event.getY()); 
            changePressedState(true); 
            return true; 
 
        case MotionEvent.ACTION_UP: 
            changePressedState(false); 
            return true; 
 
        case MotionEvent.ACTION_MOVE: 
            selectedAngle = computeAngle(event.getX(), event.getY()); 
            invalidate(); 
            return true; 
 
        default: 
            return false; 
    } 
}