Q: android rotate imageview onfling I am creating a rotating knob it works fine when I rotate it slowly in clockwise or anticlockwise direction.but if I want to select a precise value then it become hard.Because I am adding a margin of 3 on each call to onscroll(ACtion_Move). What should I do .
I think I need a precise angle between previous and current point.But it slow down the rotation.And Lets say if the user fling the wheel to right or left direction then what should I add to angle to rotate it smoothly in respective direction.
Thanks in advance for you time.
public class RotaryKnobView extends ImageView {

private float angle = 0f;
private float theta_old = 0f;

// width of imageview
private float width;
// height of imageview
private float height;
private final static int TOTAL_WEIGHT = 26;
private final static double TOTAL_ANGLE = 360;
private final static double MARGIN_BT_WEIGHT = (TOTAL_ANGLE / TOTAL_WEIGHT);
private final static double STARTING_WEIGHT = 5.0d;
private WeightPicker_Activity mContext = null;
private RotaryKnobListener listener;
private static double Weight_Picked = 0d;
private static final String TAG = "RotaryKnobView";
private Bitmap mBitmap = null;
private GestureDetector mDetector = null;

public interface RotaryKnobListener {
    public void onKnobChanged(int arg);

    public void onWeightPicked(double weightPicked);

}

public void setKnobListener(RotaryKnobListener l) {
    listener = l;
}

public RotaryKnobView(Context context) {
    super(context);
    this.mContext = (WeightPicker_Activity) context;
    initialize();
}

public RotaryKnobView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.mContext = (WeightPicker_Activity) context;
    initialize();
}

public RotaryKnobView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.mContext = (WeightPicker_Activity) context;
    initialize();
}

private float getTheta(float x, float y) {
    float sx = x - (width / 2.0f);
    float sy = y - (height / 2.0f);

    float length = (float) Math.sqrt(sx * sx + sy * sy);
    float nx = sx / length;
    float ny = sy / length;
    float theta = (float) Math.atan2(ny, nx);

    final float rad2deg = (float) (180.0 / Math.PI);
    float theta2 = theta * rad2deg;

    return (theta2 < 0) ? theta2 + 360.0f : theta2;
}

public void setBitmap(Bitmap mBitmap) {
    this.mBitmap = mBitmap;
    this.setImageBitmap(this.mBitmap);
}

public void getBitmapWidth() {
    this.mBitmap.getWidth();
}

public void getBitmapHeight() {
    this.mBitmap.getHeight();
}

private boolean isRotationStarted = false;

public void initialize() {

    // this.setImageResource(R.drawable.final_new_a);
    mDetector = new GestureDetector(mContext, new MyGestureRecognizer());
    setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            // reset angle theta after one complete rotation

            int action = event.getAction();
            int actionCode = action & MotionEvent.ACTION_MASK;
            if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
                float x = event.getX(0);
                float y = event.getY(0);
                theta_old = getTheta(x, y);
                Log.i("theta_old", theta_old + "");
            } else if (actionCode == MotionEvent.ACTION_MOVE) {
                invalidate();

                float x = event.getX(0);
                float y = event.getY(0);

                float theta = getTheta(x, y);
                float delta_theta = theta - theta_old;

                theta_old = theta;

                // here we determine the direction of rotation
                int direction = (delta_theta > 0) ? 1 : -1;

                if (direction > 0) {
                    // Log.i("Direction > 1", direction + "");

                } else {
                    // Log.i("Direction < 1", direction + "");

                }

                angle += 3 * direction;
                mixMixCondition();
                // Log.i("Current Weight",
                // "Weight"
                // + (((-1) * (angle / MARGIN_BT_WEIGHT)) + STARTING_WEIGHT)
                // + "");
                Weight_Picked = (((-1) * (angle / MARGIN_BT_WEIGHT)) + STARTING_WEIGHT);
                Log.i("angle", "angle" + angle);

                notifyListener(direction);
                notifyListener(Weight_Picked);
                RotaryKnobView.this.mContext.zoomImageView
                        .setImageBitmap(cropImage(
                                RotaryKnobView.this.mContext.mWheelRelativeLayout
                                        .getWidth() / 2, 0));

            }
            mDetector.onTouchEvent(event);
            return true;
        }

    });
}

private void mixMixCondition() {

    // stop to rotate behind 5.(min value)
    if (angle >= 0) {
        angle = 0;
    }
    // stop to rotate behind 30.(max value)
    // for anticlockwise rotation
    if (angle < (-360 + MARGIN_BT_WEIGHT)) {
        angle = (float) (-360 + MARGIN_BT_WEIGHT);
        // Toast.makeText(RotaryKnobView.this.mContext,
        // "Complete anticlockwise", Toast.LENGTH_SHORT).show();
    }

    // for clockwise rotation
    if (angle > 360) {
        angle = 0;
        // Toast.makeText(RotaryKnobView.this.mContext,
        // "Complete Clockwise",
        // Toast.LENGTH_SHORT).show();
    }

}

private void notifyListener(int arg) {
    if (null != listener)
        listener.onKnobChanged(arg);
}

private void notifyListener(double weight_picked) {
    if (null != listener) {
        listener.onWeightPicked(weight_picked);
    }
}

protected void onDraw(Canvas c) {
    c.rotate(angle, this.getWidth() / 2, this.getHeight() / 2);
    width = this.getWidth();
    height = this.getHeight();

    // LogInfo.showLogInfo(TAG, "Width = " + width);
    // LogInfo.showLogInfo(TAG, "Height" + height);
    // LogInfo.showLogInfo("ondrawImageView", "ondrawImageView");
    this.mContext.zoomImageView.setImageBitmap(cropImage(
            mContext.mWheelRelativeLayout.getWidth() / 2, 0));
    super.onDraw(c);

}

public Bitmap cropImage(int x, int y) {
    Matrix matrix = new Matrix();
    this.mContext.mWheelRelativeLayout.buildDrawingCache();
    this.mContext.mainBitmap = this.mContext.mWheelRelativeLayout
            .getDrawingCache();
    Bitmap croppedBitmap = Bitmap.createBitmap(this.mContext.mainBitmap,
            x - 50, y + 15, 100, 130, matrix, true);
    // Bitmap croppedBitmap2 = Bitmap.createBitmap(croppedBitmap, x-30,
    // y+15, 100, 120, matrix,true);
    this.mContext.mWheelRelativeLayout.invalidate();
    return croppedBitmap;
}

/**
 * @return The angle of the unit circle with the image view's center
 */
private double getAngle(double xTouch, double yTouch) {
    double x = xTouch - (this.getWidth() / 2d);
    double y = this.getHeight() - yTouch - (this.getHeight() / 2d);

    switch (getQuadrant(x, y)) {
    case 1:
        return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

    case 2:
    case 3:
        return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);

    case 4:
        return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

    default:
        // ignore, does not happen
        return 0;
    }
}

/**
 * @return The selected quadrant.
 */
private static int getQuadrant(double x, double y) {
    if (x >= 0) {
        return y >= 0 ? 1 : 4;
    } else {
        return y >= 0 ? 2 : 3;
    }
}

private class MyGestureRecognizer extends SimpleOnGestureListener {

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        LogInfo.showLogInfo("onFling", "VelocityX" + velocityX);
        LogInfo.showLogInfo("onFling", "VelocityY" + velocityY);
        LogInfo.showLogInfo("onFling", "onFling");

        return super.onFling(e1, e2, velocityX, velocityY);
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        return super.onScroll(e1, e2, distanceX, distanceY);
    }

   }
     }
    }

